diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c147f97..20545be 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -41,6 +41,34 @@ jobs: - name: Run codeception tests. run: docker exec yii2-apache vendor/bin/codecept run + test-caddy: + runs-on: ubuntu-latest + + steps: + - name: Checkout. + uses: actions/checkout@v4 + + - name: Install docker compose. + run: | + sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + docker-compose --version + + - name: Build and start containers. + run: docker-compose -f docker-compose.caddy.yml up -d --build + + - name: Wait for container to be ready. + run: | + echo "Waiting 30 seconds for container initialization..." + sleep 30 + docker logs yii2-caddy + + - name: Codeception build. + run: docker exec yii2-caddy vendor/bin/codecept build + + - name: Run codeception tests. + run: docker exec yii2-caddy vendor/bin/codecept run + test-frankenphp: runs-on: ubuntu-latest @@ -66,7 +94,7 @@ jobs: - name: Codeception build. run: docker exec yii2-frankenphp vendor/bin/codecept build - - name: Run codeception build and tests. + - name: Run codeception tests. run: docker exec yii2-frankenphp vendor/bin/codecept run test-nginx: @@ -94,5 +122,5 @@ jobs: - name: Codeception build. run: docker exec yii2-nginx vendor/bin/codecept build - - name: Run codeception build and tests. + - name: Run codeception tests. run: docker exec yii2-nginx vendor/bin/codecept run diff --git a/README.md b/README.md index 4f25dfb..16382aa 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ A modern, Bootstrap 5-powered Yii2 application template designed for rapid web-a ## Environment support [![Apache](https://img.shields.io/badge/apache-%23D42029.svg?style=for-the-badge&label=docker&logo=apache&logoColor=white)](docker-compose.yml) +[![Caddy](https://img.shields.io/badge/caddy-%231F88C0.svg?style=for-the-badge&label=docker&logo=caddy&logoColor=white)](docker-compose.caddy.yml) [![FrankenPHP](https://img.shields.io/badge/frankenphp-%23FF6B35.svg?style=for-the-badge&label=docker&logo=php&logoColor=white)](docker-compose.frankenphp.yml) [![Nginx](https://img.shields.io/badge/nginx-%23009639.svg?style=for-the-badge&label=docker&logo=nginx&logoColor=white)](docker-compose.nginx.yml) @@ -102,6 +103,9 @@ php -S localhost:8080 -t public # For Apache docker-compose up -d +# For Caddy +docker-compose -f docker-compose.caddy.yml up -d + # For FrankenPHP docker-compose -f docker-compose.frankenphp.yml up -d @@ -118,13 +122,16 @@ After starting the server, you can access your application in your web browser. http://localhost:8080/ # For Apache -http://localhost:8080/ +https://localhost:8443/ + +# For Caddy +https://localhost:8444/ # For FrankenPHP -http://localhost:8081/ +https://localhost:8445/ # For Nginx -http://localhost:8082/ +https://localhost:8446/ ``` ### Basic usage diff --git a/docker-compose.caddy.yml b/docker-compose.caddy.yml new file mode 100644 index 0000000..5b6c32a --- /dev/null +++ b/docker-compose.caddy.yml @@ -0,0 +1,34 @@ +services: + yii2-caddy: + build: + args: + USER_ID: ${USER_ID:-1000} + GROUP_ID: ${GROUP_ID:-1000} + USER_NAME: ${USER_NAME:-www-data} + GROUP_NAME: ${GROUP_NAME:-www-data} + context: . + dockerfile: docker/caddy/Dockerfile + container_name: yii2-caddy + env_file: + - .env + environment: + TZ: "UTC" + YII_DEBUG: "${YII_DEBUG:-false}" + YII_ENV: "${YII_ENV:-prod}" + ports: + - '8081:80' + - '8444:443' + - '8444:443/udp' + restart: always + tty: true + volumes: + - ./:/app + - caddy_config:/config + - caddy_data:/data + - composer_cache:/var/www/.composer/cache + working_dir: /app + +volumes: + caddy_data: + caddy_config: + composer_cache: diff --git a/docker-compose.frankenphp.yml b/docker-compose.frankenphp.yml index 26caad4..bfda61b 100644 --- a/docker-compose.frankenphp.yml +++ b/docker-compose.frankenphp.yml @@ -17,17 +17,17 @@ services: YII_DEBUG: "${YII_DEBUG:-false}" YII_ENV: "${YII_ENV:-prod}" ports: - - '8081:80' - - '8444:443' - - '8444:443/udp' + - '8082:80' + - '8445:443' + - '8445:443/udp' restart: always + tty: true volumes: - ./:/app - caddy_config:/config - caddy_data:/data - composer_cache:/var/www/.composer/cache working_dir: /app - tty: true volumes: caddy_data: diff --git a/docker-compose.nginx.yml b/docker-compose.nginx.yml index bb983a4..375858a 100644 --- a/docker-compose.nginx.yml +++ b/docker-compose.nginx.yml @@ -17,8 +17,8 @@ services: YII_DEBUG: "${YII_DEBUG:-false}" YII_ENV: "${YII_ENV:-prod}" ports: - - '8082:80' - - '8445:443' + - '8083:80' + - '8446:443' restart: always tty: true volumes: diff --git a/docker/caddy/Caddyfile b/docker/caddy/Caddyfile new file mode 100644 index 0000000..5d365ed --- /dev/null +++ b/docker/caddy/Caddyfile @@ -0,0 +1,65 @@ +{ + # Global options + auto_https off +} + +# HTTPS server block using mkcert certificates +https://localhost:443 { + # Specify mkcert certificates + tls /app/docker/ssl/localhost.pem /app/docker/ssl/localhost-key.pem + + # Document root + root * /app/public + + # Enable PHP processing with FPM + php_fastcgi unix//var/run/php/php-fpm.sock { + index index.php + try_files {path} {path}/index.php =404 + } + + # Security headers + header { + X-Frame-Options "SAMEORIGIN" + X-XSS-Protection "1; mode=block" + X-Content-Type-Options "nosniff" + Strict-Transport-Security "max-age=31536000; includeSubDomains" + -Server + } + + # Logging + log { + output stdout + format console + } + + # Handle static files + @static { + file + path *.css *.js *.png *.jpg *.jpeg *.gif *.ico *.svg *.woff *.woff2 *.ttf *.eot + } + handle @static { + header Cache-Control "public, max-age=31536000" + file_server + } + + # Block access to sensitive directories + @forbidden { + path /.git/* /vendor/* /runtime/* /.env* + } + respond @forbidden 404 + + # Deny PHP execution in assets directory (Yii2 security) + @assets_php { + path /assets/*.php + } + respond @assets_php 403 + + # Try files for Yii2 URL rewriting + try_files {path} {path}/ /index.php?{query} +} + +# HTTP server block - redirect to HTTPS +http://localhost:80 { + # Redirect all HTTP traffic to HTTPS + redir https://localhost:8444{uri} permanent +} diff --git a/docker/caddy/Dockerfile b/docker/caddy/Dockerfile new file mode 100644 index 0000000..5f53aec --- /dev/null +++ b/docker/caddy/Dockerfile @@ -0,0 +1,93 @@ +FROM php:8.4-fpm + +# Build arguments for user/group +ARG USER_ID=1000 +ARG GROUP_ID=1000 +ARG USER_NAME=www-data +ARG GROUP_NAME=www-data + +# Set document root to /app/public (Yii2 structure) +WORKDIR /app + +# Install required system packages for PHP extensions for Yii 2.0 Framework +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ +RUN install-php-extensions \ + bcmath \ + @composer \ + exif \ + gd \ + imagick \ + intl \ + opcache \ + pdo_mysql \ + pdo_pgsql \ + soap \ + xdebug \ + zip + +# Set composer environment +ENV COMPOSER_ALLOW_SUPERUSER=1 + +# Change PHP config +COPY docker/php/php.ini /usr/local/etc/php/conf.d/base.ini + +# Install supervisord, gosu, and Node.js (version simple) +RUN apt-get update && apt-get install -y --no-install-recommends \ + supervisor \ + curl \ + gosu \ + debian-keyring \ + debian-archive-keyring \ + apt-transport-https \ + && curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \ + && apt-get install -y nodejs \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# Install Caddy (standalone) +RUN curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg \ + && curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list \ + && apt-get update \ + && apt-get install -y caddy \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# Change web server config +COPY docker/caddy/Caddyfile /etc/caddy/Caddyfile + +# Apply the user/group IDs to www-data +RUN usermod -u ${USER_ID} www-data && groupmod -g ${GROUP_ID} www-data + +# Create composer and npm cache directories with proper ownership +RUN mkdir -p /var/www/.composer/cache /var/www/.npm && \ + chown -R www-data:www-data /var/www/.composer /var/www/.npm + +# Configure PHP-FPM to use Unix socket +RUN sed -i 's|^listen = 127.0.0.1:9000|listen = /var/run/php/php-fpm.sock|' /usr/local/etc/php-fpm.d/www.conf \ + && sed -i 's|^;listen.owner = www-data|listen.owner = www-data|' /usr/local/etc/php-fpm.d/www.conf \ + && sed -i 's|^;listen.group = www-data|listen.group = www-data|' /usr/local/etc/php-fpm.d/www.conf \ + && sed -i 's|^;listen.mode = 0660|listen.mode = 0660|' /usr/local/etc/php-fpm.d/www.conf \ + && mkdir -p /var/run/php \ + && chown www-data:www-data /var/run/php + +# Copy supervisord program configs +COPY docker/supervisord/conf.d/caddy-php-fpm.conf /etc/supervisord/conf.d/caddy-php-fpm.conf + +# Copy queue worker config uncommented for use with yii2-queue +#COPY docker/supervisor/available/queue.conf /etc/supervisor/available/queue.conf + +# Copy scripts +COPY docker/init.sh /usr/local/bin/init.sh +COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh + +# Make scripts executable and validate +RUN chmod +x /usr/local/bin/init.sh /usr/local/bin/entrypoint.sh && \ + # Convert any Windows line endings + sed -i 's/\r$//' /usr/local/bin/init.sh /usr/local/bin/entrypoint.sh && \ + # Test that scripts have valid syntax + bash -n /usr/local/bin/init.sh && \ + bash -n /usr/local/bin/entrypoint.sh && \ + echo "✓ Scripts validated successfully..." + +# Use ENTRYPOINT to guarantee execution +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/docker/supervisord/conf.d/caddy-php-fpm.conf b/docker/supervisord/conf.d/caddy-php-fpm.conf new file mode 100644 index 0000000..eaea1f5 --- /dev/null +++ b/docker/supervisord/conf.d/caddy-php-fpm.conf @@ -0,0 +1,21 @@ +[program:php-fpm] +command=/usr/local/sbin/php-fpm --nodaemonize +autorestart=true +autostart=true +priority=5 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +user=root + +[program:caddy] +command=/usr/bin/caddy run --config /etc/caddy/Caddyfile +autorestart=true +autostart=true +priority=10 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +user=www-data