From 4e58f0b6a9cf8fa59c50996925080aba9dbc1544 Mon Sep 17 00:00:00 2001 From: myrmidex Date: Mon, 29 Dec 2025 17:16:42 +0100 Subject: [PATCH] add provisional docker files --- Dockerfile | 122 ++++++++++++++++++++++++++++++++++++++ Dockerfile.dev | 127 ++++++++++++++++++++++++++++++++++++++++ docker-compose.prod.yml | 99 +++++++++++++++++++++++++++++++ docker-compose.yml | 119 +++++++++++++++++++++++++++++++++++++ 4 files changed, 467 insertions(+) create mode 100644 Dockerfile create mode 100644 Dockerfile.dev create mode 100644 docker-compose.prod.yml create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6dd97e5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,122 @@ +# Production Dockerfile with FrankenPHP +FROM dunglas/frankenphp:latest-php8.4-alpine + +# Install system dependencies +RUN apk add --no-cache \ + nodejs \ + npm \ + git \ + mysql-client + +# Install PHP extensions +RUN install-php-extensions \ + pdo_mysql \ + opcache \ + zip \ + gd \ + intl + +# Install Composer +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer + +# Set working directory +WORKDIR /app + +# Set fixed production environment variables +ENV APP_ENV=production \ + APP_DEBUG=false \ + DB_CONNECTION=mysql \ + DB_HOST=db \ + DB_PORT=3306 \ + SESSION_DRIVER=database \ + CACHE_DRIVER=file \ + QUEUE_CONNECTION=database \ + LOG_CHANNEL=stack \ + LOG_LEVEL=error \ + MAIL_MAILER=smtp \ + MAIL_ENCRYPTION=tls + +# Copy application code first +COPY . . + +# Install PHP dependencies (production only) +RUN composer install --no-dev --no-interaction --optimize-autoloader + +# Install ALL Node dependencies (including dev for building) +RUN npm ci + +# Build frontend assets +RUN npm run build + +# Remove node_modules after build to save space +RUN rm -rf node_modules + +# Laravel optimizations +RUN php artisan config:cache \ + && php artisan route:cache \ + && composer dump-autoload --optimize + +# Set permissions +RUN chown -R www-data:www-data /app/storage /app/bootstrap/cache + +# Configure Caddy +RUN cat > /etc/caddy/Caddyfile < /start-prod.sh <<'EOF' +#!/bin/sh +set -e + +# Wait for database to be ready +echo "Waiting for database..." +for i in $(seq 1 30); do + if php artisan db:monitor --database=mysql 2>/dev/null | grep -q "OK"; then + echo "Database is ready!" + break + fi + echo "Waiting for database... ($i/30)" + sleep 2 +done + +# Run migrations +echo "Running migrations..." +php artisan migrate --force || echo "Migrations failed or already up-to-date" + +# Start FrankenPHP +exec frankenphp run --config /etc/caddy/Caddyfile +EOF + +RUN chmod +x /start-prod.sh + +# Start with our script +CMD ["/start-prod.sh"] diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000..53ac66f --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,127 @@ +# Development Dockerfile with FrankenPHP +FROM dunglas/frankenphp:latest-php8.4-alpine + +# Install system dependencies + development tools +RUN apk add --no-cache \ + nodejs \ + npm \ + git \ + mysql-client \ + vim \ + bash \ + nano + +# Install PHP extensions including xdebug for development +RUN install-php-extensions \ + pdo_mysql \ + opcache \ + zip \ + gd \ + intl \ + xdebug + +# Install Composer +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer + +# Set working directory +WORKDIR /app + +# Configure PHP for development +RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" + +# Configure Xdebug (disabled by default to reduce noise) +RUN echo "xdebug.mode=off" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ + && echo ";xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ + && echo ";xdebug.client_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ + && echo ";xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini + +# Configure Caddy for development (simpler, no worker mode) +RUN cat > /etc/caddy/Caddyfile < /start.sh <<'EOF' +#!/bin/sh +set -e + +# Create .env file if it doesn't exist +if [ ! -f ".env" ]; then + echo "Creating .env file from .env.example..." + cp .env.example .env +fi + +# Install dependencies if volumes are empty +if [ ! -f "vendor/autoload.php" ]; then + echo "Installing composer dependencies..." + composer install +fi + +# Handle node_modules with care - clean install if having issues +if [ ! -f "node_modules/.bin/vite" ]; then + echo "Installing npm dependencies..." + # Clean any remnants first + rm -rf node_modules/.* 2>/dev/null || true + rm -rf /app/.npm 2>/dev/null || true + # Fresh install with cache in tmp to avoid permission issues + npm install --cache /tmp/.npm +else + echo "Node modules already installed, skipping npm install" +fi + +# Clear Laravel caches +php artisan config:clear || true +php artisan cache:clear || true + +# Wait for database and run migrations +echo "Waiting for database..." +sleep 5 +php artisan migrate --force || echo "Migration failed or not needed" + +# Run development seeder (only in dev environment) +echo "Running development seeder..." +php artisan db:seed --class=DevelopmentSeeder --force || echo "Seeding skipped or already done" + +# Generate app key if not set +if [ -z "$APP_KEY" ] || [ "$APP_KEY" = "base64:YOUR_KEY_HERE" ]; then + echo "Generating application key..." + php artisan key:generate +fi + +# Start Vite dev server in background +npm run dev & + +# Start FrankenPHP +exec frankenphp run --config /etc/caddy/Caddyfile +EOF + +RUN chmod +x /start.sh + +# Expose ports +EXPOSE 8000 5173 + +# Use the startup script +CMD ["/start.sh"] \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..058585d --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,99 @@ +# Production Docker Compose +services: + app: + image: codeberg.org/lvl0/buckets:latest + container_name: buckets_app + restart: always + ports: + - "8000:8000" + environment: + # Required from user + APP_KEY: "${APP_KEY}" # Critical - must persist across deployments + APP_URL: "${APP_URL}" + DB_DATABASE: "${DB_DATABASE}" + DB_USERNAME: "${DB_USERNAME}" + DB_PASSWORD: "${DB_PASSWORD}" + + # Optional email configuration + MAIL_HOST: "${MAIL_HOST:-}" + MAIL_PORT: "${MAIL_PORT:-587}" + MAIL_USERNAME: "${MAIL_USERNAME:-}" + MAIL_PASSWORD: "${MAIL_PASSWORD:-}" + MAIL_FROM_ADDRESS: "${MAIL_FROM_ADDRESS:-noreply@example.com}" + volumes: + # Only persist storage in production + - app_storage:/app/storage + - app_logs:/app/storage/logs + depends_on: + - db + networks: + - buckets + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/up"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + db: + image: mariadb:11 + container_name: buckets_db + restart: always + environment: + MYSQL_DATABASE: "${DB_DATABASE}" + MYSQL_USER: "${DB_USERNAME}" + MYSQL_PASSWORD: "${DB_PASSWORD}" + MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}" + volumes: + - db_data:/var/lib/mysql + networks: + - buckets + + # Optional: Redis for production caching/sessions + # redis: + # image: redis:7-alpine + # container_name: buckets_redis + # restart: always + # command: redis-server --requirepass ${REDIS_PASSWORD} + # volumes: + # - redis_data:/data + # networks: + # - buckets + # healthcheck: + # test: ["CMD", "redis-cli", "ping"] + # interval: 30s + # timeout: 10s + # retries: 3 + + # Optional: Backup service + # backup: + # image: mariadb:11 + # container_name: buckets_backup + # restart: always + # environment: + # MYSQL_HOST: db + # MYSQL_USER: root + # MYSQL_PASSWORD: "${DB_ROOT_PASSWORD}" + # volumes: + # - ./database/backups:/backups + # - ./scripts/backup.sh:/backup.sh:ro + # entrypoint: ["/bin/sh"] + # command: ["-c", "while true; do /backup.sh; sleep 86400; done"] + # depends_on: + # - db + # networks: + # - buckets + +networks: + buckets: + driver: bridge + +volumes: + db_data: + driver: local + app_storage: + driver: local + app_logs: + driver: local + # redis_data: + # driver: local diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f8492cd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,119 @@ +# Local Development Docker Compose +version: '3.8' + +services: + app: + build: + context: . + dockerfile: Dockerfile.dev + container_name: buckets_app + restart: unless-stopped + # Remove user directive to run as root in container + # The container will handle permissions internally + ports: + - "8000:8000" # Laravel app + - "5173:5173" # Vite dev server + environment: + # Laravel + APP_NAME: "${APP_NAME:-buckets}" + APP_ENV: "${APP_ENV:-local}" + APP_KEY: "${APP_KEY:-base64:YOUR_KEY_HERE}" + APP_DEBUG: "${APP_DEBUG:-true}" + APP_URL: "${APP_URL:-http://localhost:8000}" + + # Database + DB_CONNECTION: mysql + DB_HOST: db + DB_PORT: 3306 + DB_DATABASE: "${DB_DATABASE:-buckets}" + DB_USERNAME: "${DB_USERNAME:-buckets}" + DB_PASSWORD: "${DB_PASSWORD:-buckets}" + + # Session & Cache + SESSION_DRIVER: "${SESSION_DRIVER:-file}" + CACHE_DRIVER: "${CACHE_DRIVER:-file}" + QUEUE_CONNECTION: "${QUEUE_CONNECTION:-sync}" + + # Mail (for development) + MAIL_MAILER: "${MAIL_MAILER:-log}" + + # Vite + VITE_HOST: "0.0.0.0" + volumes: + # Mount entire project for hot reload with SELinux context + - .:/app:Z + # Named volumes for performance and permission isolation + - app_vendor:/app/vendor + - app_node_modules:/app/node_modules + depends_on: + - db + networks: + - buckets + + db: + image: mariadb:11 + container_name: buckets_db + restart: unless-stopped + ports: + - "3306:3306" + environment: + MYSQL_DATABASE: "${DB_DATABASE:-buckets}" + MYSQL_USER: "${DB_USERNAME:-buckets}" + MYSQL_PASSWORD: "${DB_PASSWORD:-buckets}" + MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD:-root}" + volumes: + - db_data:/var/lib/mysql + # Initialize with SQL scripts + - ./docker/mysql-init:/docker-entrypoint-initdb.d + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 3 + networks: + - buckets + + # Optional: Mailhog for email testing + mailhog: + image: mailhog/mailhog + container_name: buckets_mailhog + restart: unless-stopped + ports: + - "1025:1025" # SMTP server + - "8025:8025" # Web UI + networks: + - buckets + + # Selenium for E2E testing with Dusk + selenium: + image: selenium/standalone-chrome:latest + container_name: buckets_selenium + restart: unless-stopped + ports: + - "4444:4444" # Selenium server + - "7900:7900" # VNC server for debugging + volumes: + - /dev/shm:/dev/shm + networks: + - buckets + environment: + - SE_VNC_PASSWORD=secret + + # Optional: Redis for caching/sessions + # redis: + # image: redis:alpine + # container_name: buckets_redis + # restart: unless-stopped + # ports: + # - "6379:6379" + # networks: + # - buckets + +networks: + buckets: + driver: bridge + +volumes: + db_data: + app_vendor: + app_node_modules: \ No newline at end of file