commit 654956a1542b5779fc67b6879fed63959fc401c2 Author: Gbanyan Date: Sun Nov 16 10:42:14 2025 +0800 chore: initialize dockerized wordpress stack diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0bb05ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Environment files +.env +.env.* +*.env + +# Local docker data volumes +/db_data/ +/wordpress_data/ +/wordpress_data/wp-config.php +/redis_data/ + +# WordPress exports / backups +*.sql +*.tar +*.tar.gz +*.tgz +*.zip + +# Editor / OS cruft +.DS_Store +Thumbs.db +.idea/ +.vscode/ + +# Log files +*.log diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c2891c --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# DigitechFlow WordPress Stack + +This repository contains a hardened Docker Compose stack for the DigitechFlow WordPress deployment. It runs MySQL, WordPress (PHP-FPM), Nginx, and Valkey (Redis-compatible cache) with sensible defaults. + +## Prerequisites +- Docker Engine + Docker Compose V2 +- Traefik network (`traefik_default`) already present for routing/SSL +- Host directories writable for `db_data/`, `wordpress_data/`, and `redis_data/` + +## Getting Started +1. Copy the environment template and fill in secrets: + ```bash + cp .env.example .env + # Edit .env to use strong unique values (DB creds, salts) + ``` +2. Bring up the stack: + ```bash + docker compose up -d + ``` +3. Access WordPress via the configured Traefik domain (e.g., https://digitechflow.com). + +## Services +- **db**: `mysql:latest` with persistent volume `db_data/`. +- **wordpress**: `wordpress:php8.3-fpm` serving PHP over FastCGI. +- **wordpress_nginx**: `nginx:latest` front-end with custom config tuned for Traefik and FastCGI. +- **redis**: `valkey/valkey:latest` for object caching with persistence and healthcheck. + +## Configuration Highlights +- Secrets and database settings sourced from `.env`; the template (`.env.example`) documents required keys. +- `config/nginx/default.conf` contains gzip, caching, and FastCGI tuning. Adjust if you need custom routes. +- `wordpress.ini` sets PHP limits and Opcache recommendations. +- Local data directories (`db_data/`, `wordpress_data/`, `redis_data/`) plus `wp-config.php` are gitignored to prevent leaking content/secrets. + +## Operations +- Update images: `docker compose pull && docker compose up -d`. +- View logs: `docker compose logs -f `. +- Run WordPress CLI tasks: `docker compose exec wordpress wp ` (install WP-CLI first if needed). + +## Security Notes +- Always use unique, strong passwords in `.env` and rotate them periodically. +- Regenerate WordPress auth salts via https://api.wordpress.org/secret-key/1.1/salt/ and store them in `.env`. +- Ensure Traefik enforces HTTPS and apply rate limiting/WAF middleware as needed. +- Schedule backups (database dumps + `wordpress_data`) off-host; the stack does not include automated backups by default. + +## Troubleshooting +- Bad gateway from Nginx usually means PHP-FPM isn’t reachable; check `docker compose logs wordpress wordpress_nginx`. +- If Compose warns about `version: '3'`, you can remove that line—it’s optional with Compose V2. +- Ensure the Traefik network exists: `docker network ls | grep traefik_default`. diff --git a/config/nginx/default.conf b/config/nginx/default.conf new file mode 100644 index 0000000..26b51f8 --- /dev/null +++ b/config/nginx/default.conf @@ -0,0 +1,49 @@ +server { + listen 80; + server_name _; + + root /var/www/html; + index index.php index.html; + + # Trust real client IPs forwarded by Traefik + set_real_ip_from 172.19.0.0/16; + set_real_ip_from 172.21.0.0/16; + real_ip_header X-Forwarded-For; + real_ip_recursive on; + + # Basic compression for text assets + gzip on; + gzip_types text/plain text/css application/json application/javascript application/xml+rss image/svg+xml; + gzip_comp_level 5; + + location / { + try_files $uri $uri/ /index.php?$args; + } + + location ~ \.php$ { + include fastcgi_params; + fastcgi_pass wordpress:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param HTTP_X_FORWARDED_FOR $proxy_add_x_forwarded_for; + fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto; + fastcgi_param HTTPS $http_x_forwarded_proto; + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; + fastcgi_read_timeout 120s; + fastcgi_send_timeout 120s; + fastcgi_connect_timeout 60s; + } + + location ~* \.(?:css|js|jpg|jpeg|gif|png|svg|ico|webp|avif)$ { + expires 30d; + add_header Cache-Control "public, no-transform"; + access_log off; + try_files $uri =404; + } + + client_max_body_size 64m; + sendfile on; + keepalive_timeout 65; +} diff --git a/config/redis.conf b/config/redis.conf new file mode 100644 index 0000000..134bd69 --- /dev/null +++ b/config/redis.conf @@ -0,0 +1,12 @@ +# Persistent Valkey configuration tuned for WordPress object cache. +appendonly yes +appendfsync everysec +save 900 1 +save 300 10 +save 60 10000 +maxmemory 256mb +maxmemory-policy allkeys-lru +protected-mode no +bind 0.0.0.0 +port 6379 +dir /data diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..443b6a5 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,99 @@ +services: + # MySQL Service + db: + image: mysql:latest + container_name: digitechflow_db + volumes: + - ./db_data:/var/lib/mysql + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + networks: + - wordpress_network + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 5s + timeout: 5s + retries: 10 + + # WordPress PHP-FPM Service + wordpress: + depends_on: + db: + condition: service_healthy + image: wordpress:php8.3-fpm + container_name: digitechflow_wordpress + restart: unless-stopped + volumes: + - ./wordpress_data:/var/www/html + - ./wordpress.ini:/usr/local/etc/php/conf.d/wordpress.ini + expose: + - "9000" + environment: + WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST} + WORDPRESS_DB_USER: ${WORDPRESS_DB_USER} + WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD} + WORDPRESS_DB_NAME: ${WORDPRESS_DB_NAME} + WORDPRESS_REDIS_HOST: ${WORDPRESS_REDIS_HOST} + networks: + - wordpress_network + extra_hosts: + - "host.docker.internal:host-gateway" # For crowdsec plugin to connect to host crowdsec api +# Nginx front-end for WordPress (Traefik faces this container) + wordpress_nginx: + depends_on: + wordpress: + condition: service_started + image: nginx:latest + container_name: digitechflow_nginx + restart: unless-stopped + volumes: + - ./wordpress_data:/var/www/html:ro + - ./config/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro + networks: + - wordpress_network + - traefik_default + labels: + - "traefik.enable=true" + - "traefik.http.routers.digitechflow.rule=Host(`digitechflow.com`)" + - "traefik.http.routers.digitechflow.entrypoints=websecure" + - "traefik.http.routers.digitechflow.middlewares=crowdsec@file,retry-fast@file" + - "traefik.http.routers.digitechflow.tls.certresolver=letsencrypt" + - "traefik.http.services.digitechflow.loadbalancer.server.port=80" + - "traefik.http.services.digitechflow.loadbalancer.serversTransport=fast-upstreams@file" + - "traefik.docker.network=traefik_default" + + redis: + image: valkey/valkey:latest + container_name: digitechflow_valkey + restart: unless-stopped + volumes: + - ./redis_data:/data + - ./config/redis.conf:/usr/local/etc/redis/redis.conf:ro + command: ["valkey-server", "/usr/local/etc/redis/redis.conf"] + networks: + - wordpress_network + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 +# Volumes for persistent data +volumes: + db_data: + wordpress_data: + redis_data: + +# Network for communication between services +networks: + wordpress_network: + driver: bridge + ipam: + config: + - subnet: 172.21.0.0/16 + gateway: 172.21.0.1 + traefik_default: + external: true # Assumes Traefik uses an existing network diff --git a/wordpress.ini b/wordpress.ini new file mode 100644 index 0000000..6854bcf --- /dev/null +++ b/wordpress.ini @@ -0,0 +1,12 @@ +file_uploads = On +memory_limit = 512M +upload_max_filesize = 64M +post_max_size = 64M +max_execution_time = 300 +max_input_time = 1000 +; Opcache tuning for better PHP-FPM performance +opcache.enable=1 +opcache.memory_consumption=192 +opcache.interned_strings_buffer=16 +opcache.max_accelerated_files=10000 +opcache.validate_timestamps=0