| .gitignore | ||
| backup.sh | ||
| compose.override.yml | ||
| compose.yml | ||
| example.env | ||
| README.md | ||
WordPress Docker Compose Setup
This repository contains a Docker Compose setup for running WordPress - a popular open-source content management system. This setup uses the official WordPress Docker image and is designed to run behind a reverse proxy.
Architecture
This setup runs WordPress with the following components:
- WordPress: Main application using the official WordPress Docker image
- MariaDB: Database server for data persistence
All data and configuration are persisted through Docker volumes. This setup is designed to run behind a reverse proxy (nginx setup is separate).
Prerequisites
- Docker and Docker Compose installed
- Reverse proxy setup (nginx) for SSL termination and domain routing
- Firewall configured (recommended)
Quick Start
-
Clone or download this repository
git clone <repository-url> cd wordpress-sauna -
Create environment file
cp example.env .env -
Generate WordPress security keys
# Visit https://api.wordpress.org/secret-key/1.1/salt/ to generate keys # This will output something like:define('AUTH_KEY', '>>9_)AC6LZ{#Ij,-nqEj5?Dj5,i$k>^g=M>])%jan]RTm:5:.fo^+O-]Szi_mS,T'); define('SECURE_AUTH_KEY', 'lwk&Z|]b(zJtC(q<kG~t/MkC}1a$oHthiyKCidZGb8h==6:WtRJ=`qM]aqP._/ ('); define('LOGGED_IN_KEY', 'w$:}?ZVHH8vjHv/M1!Zmnbg$=E&?<A>9}!,+|y9l+CWc0$O@3;XrmY}(RT5t?2Az'); define('NONCE_KEY', 'CY4[>ybPS@1VNj0aZu2!KF!JoN981>zhsFF/h),hlHp_/)T|=Y*y|A3} 1ki-=cB'); define('AUTH_SALT', 'A$H9cr`m*^[MFf7yhn(Cb;`{@ECx|_[KTWEI3Fiw)lY1DhD%@lP9gifFn]>*odvr'); define('SECURE_AUTH_SALT', '9vZ^~-X*5:J~1.pIU.Q_slE32T>tfnOo7MN?K5-~8%UQvxm]E,4-j(1p?7UOT{Nw'); define('LOGGED_IN_SALT', '.UOxzY9W0v#brc+|E.l EcQ|J-.:u))&a/bK@+Hfu>[C<p7h.>42xb!?hcXaWZI-'); define('NONCE_SALT', 'hghRm69U|+R[8P!3+J-GuL3M|`v*Hl,dVz46x,#P]~HOtYJ2?4_+-(+)pW(&G_v@'); -
Configure environment variables
# Edit .env with your values WORDPRESS_VERSION=8.4 MARIADB_DATABASE=wordpress MARIADB_USER=wordpress MARIADB_PASSWORD=wordpress_password MARIADB_ROOT_PASSWORD=your_root_password_here # Copy the generated keys from step 3 and set them in .env: WORDPRESS_AUTH_KEY='>>9_)AC6LZ{#Ij,-nqEj5?Dj5,i$k>^g=M>])%jan]RTm:5:.fo^+O-]Szi_mS,T' WORDPRESS_SECURE_AUTH_KEY='lwk&Z|]b(zJtC(q<kG~t/MkC}1a$oHthiyKCidZGb8h==6:WtRJ=`qM]aqP._/ (' WORDPRESS_LOGGED_IN_KEY='w$:}?ZVHH8vjHv/M1!Zmnbg$=E&?<A>9}!,+|y9l+CWc0$O@3;XrmY}(RT5t?2Az' WORDPRESS_NONCE_KEY='CY4[>ybPS@1VNj0aZu2!KF!JoN981>zhsFF/h),hlHp_/)T|=Y*y|A3} 1ki-=cB' WORDPRESS_AUTH_SALT='A$H9cr`m*^[MFf7yhn(Cb;`{@ECx|_[KTWEI3Fiw)lY1DhD%@lP9gifFn]>*odvr' WORDPRESS_SECURE_AUTH_SALT='9vZ^~-X*5:J~1.pIU.Q_slE32T>tfnOo7MN?K5-~8%UQvxm]E,4-j(1p?7UOT{Nw' WORDPRESS_LOGGED_IN_SALT='.UOxzY9W0v#brc+|E.l EcQ|J-.:u))&a/bK@+Hfu>[C<p7h.>42xb!?hcXaWZI-' WORDPRESS_NONCE_SALT='hghRm69U|+R[8P!3+J-GuL3M|`v*Hl,dVz46x,#P]~HOtYJ2?4_+-(+)pW(&G_v@' -
Start the services
docker compose up -dDocker Compose automatically applies
compose.override.yml. This attaches thewordpressservice to the externalreverse-proxynetwork (and keeps the example port mapping commented for production friendliness). -
Access WordPress
- Website: https://yourdomain.com
- Admin panel: https://yourdomain.com/wp-admin/
- Important: Complete the WordPress installation and change default passwords!
Configuration
Compose Override
This project includes a compose.override.yml that Docker Compose loads automatically.
It connects the wordpress service to the external reverse-proxy network for access
via the nginx reverse proxy.
- Ensure the
reverse-proxynetwork exists (usually created by the nginx setup). - Prefer defining network membership in your Compose files over manual
docker network connectcommands.
Environment Variables
| Variable | Description | Example |
|---|---|---|
WORDPRESS_VERSION |
WordPress version to use | 8.4 |
MARIADB_DATABASE |
MariaDB database name | wordpress |
MARIADB_USER |
MariaDB username | wordpress |
MARIADB_PASSWORD |
MariaDB password | wordpress_password |
MARIADB_ROOT_PASSWORD |
MariaDB root password | your_root_password_here |
WORDPRESS_AUTH_KEY |
WordPress authentication key | Generated key |
WORDPRESS_SECURE_AUTH_KEY |
WordPress secure authentication key | Generated key |
WORDPRESS_LOGGED_IN_KEY |
WordPress logged in key | Generated key |
WORDPRESS_NONCE_KEY |
WordPress nonce key | Generated key |
WORDPRESS_AUTH_SALT |
WordPress authentication salt | Generated salt |
WORDPRESS_SECURE_AUTH_SALT |
WordPress secure authentication salt | Generated salt |
WORDPRESS_LOGGED_IN_SALT |
WordPress logged in salt | Generated salt |
WORDPRESS_NONCE_SALT |
WordPress nonce salt | Generated salt |
Volume Mappings
| Host Path | Container Path | Purpose |
|---|---|---|
./wordpress |
/var/www/html |
WordPress files and uploads |
./mariadb |
/var/lib/mysql |
MariaDB data |
Ports
| Service | Host Port | Container Port | Purpose |
|---|---|---|---|
| WordPress | 8080 | 80 | Web interface (development) |
| MariaDB | - | 3306 | Database (internal) |
Note: Port 8080 is exposed for development/testing. For production, remove the port mapping and access only through the reverse proxy.
Services
WordPress
- Image:
wordpress:php${WORDPRESS_VERSION}(official WordPress image) - Features: Full WordPress CMS with plugin and theme support
- Access: Through reverse proxy network or port 8080 (development)
- Network: Connected to
reverse-proxynetwork for external access - Updates: Handled through WordPress web interface (as per official documentation)
MariaDB
- Image:
mariadb:10.5.18 - Purpose: Database server for WordPress data
- Persistence: Data stored in
./mariadbvolume - Security: Password-protected, internal network only
Network Configuration
Docker Networks
The setup uses the reverse-proxy network to communicate with the nginx reverse proxy:
# Create network if not exists (usually done by nginx setup)
docker network create reverse-proxy
# Connect WordPress to the network
docker network connect reverse-proxy wordpress
Note: Using compose.override.yml, wordpress is already attached to reverse-proxy.
Defining the network under services.wordpress.networks in Compose keeps the
configuration persistent across restarts.
Reverse Proxy Integration
This WordPress setup is designed to work with a separate nginx reverse proxy that:
- Handles SSL termination
- Routes traffic based on domain names
- Provides PROXY protocol support
- Manages client IP forwarding
WordPress Configuration
Security Keys
WordPress requires unique security keys for enhanced security. These should be generated
before starting the containers and set in your .env file.
Important: Never use the default keys from the example file in production!
-
Online generator (recommended): Visit https://api.wordpress.org/secret-key/1.1/salt/
-
WordPress CLI (after initial setup):
docker compose exec wordpress wp config shuffle-salts -
Manual generation: Use a secure random generator to create unique keys
Example of generated keys:
define('AUTH_KEY', '>>9_)AC6LZ{#Ij,-nqEj5?Dj5,i$k>^g=M>])%jan]RTm:5:.fo^+O-]Szi_mS,T');
define('SECURE_AUTH_KEY', 'lwk&Z|]b(zJtC(q<kG~t/MkC}1a$oHthiyKCidZGb8h==6:WtRJ=`qM]aqP._/ (');
define('LOGGED_IN_KEY', 'w$:}?ZVHH8vjHv/M1!Zmnbg$=E&?<A>9}!,+|y9l+CWc0$O@3;XrmY}(RT5t?2Az');
define('NONCE_KEY', 'CY4[>ybPS@1VNj0aZu2!KF!JoN981>zhsFF/h),hlHp_/)T|=Y*y|A3} 1ki-=cB');
define('AUTH_SALT', 'A$H9cr`m*^[MFf7yhn(Cb;`{@ECx|_[KTWEI3Fiw)lY1DhD%@lP9gifFn]>*odvr');
define('SECURE_AUTH_SALT', '9vZ^~-X*5:J~1.pIU.Q_slE32T>tfnOo7MN?K5-~8%UQvxm]E,4-j(1p?7UOT{Nw');
define('LOGGED_IN_SALT', '.UOxzY9W0v#brc+|E.l EcQ|J-.:u))&a/bK@+Hfu>[C<p7h.>42xb!?hcXaWZI-');
define('NONCE_SALT', 'hghRm69U|+R[8P!3+J-GuL3M|`v*Hl,dVz46x,#P]~HOtYJ2?4_+-(+)pW(&G_v@');
Copy these values to your .env file:
WORDPRESS_AUTH_KEY='>>9_)AC6LZ{#Ij,-nqEj5?Dj5,i$k>^g=M>])%jan]RTm:5:.fo^+O-]Szi_mS,T'
WORDPRESS_SECURE_AUTH_KEY='lwk&Z|]b(zJtC(q<kG~t/MkC}1a$oHthiyKCidZGb8h==6:WtRJ=`qM]aqP._/ ('
WORDPRESS_LOGGED_IN_KEY='w$:}?ZVHH8vjHv/M1!Zmnbg$=E&?<A>9}!,+|y9l+CWc0$O@3;XrmY}(RT5t?2Az'
WORDPRESS_NONCE_KEY='CY4[>ybPS@1VNj0aZu2!KF!JoN981>zhsFF/h),hlHp_/)T|=Y*y|A3} 1ki-=cB'
WORDPRESS_AUTH_SALT='A$H9cr`m*^[MFf7yhn(Cb;`{@ECx|_[KTWEI3Fiw)lY1DhD%@lP9gifFn]>*odvr'
WORDPRESS_SECURE_AUTH_SALT='9vZ^~-X*5:J~1.pIU.Q_slE32T>tfnOo7MN?K5-~8%UQvxm]E,4-j(1p?7UOT{Nw'
WORDPRESS_LOGGED_IN_SALT='.UOxzY9W0v#brc+|E.l EcQ|J-.:u))&a/bK@+Hfu>[C<p7h.>42xb!?hcXaWZI-'
WORDPRESS_NONCE_SALT='hghRm69U|+R[8P!3+J-GuL3M|`v*Hl,dVz46x,#P]~HOtYJ2?4_+-(+)pW(&G_v@'
wp-config.php
The WordPress configuration is handled through environment variables. If you need custom configuration, you can:
-
Mount a custom wp-config.php:
volumes: - ./wp-config.php:/var/www/html/wp-config.php -
Use WordPress CLI for advanced configuration:
docker compose exec wordpress wp config set WP_DEBUG true
Updates and Maintenance
WordPress Updates
According to the official WordPress Docker image documentation, WordPress updates are handled through the web interface:
- Access WordPress admin: https://yourdomain.com/wp-admin/
- Check for updates: Dashboard → Updates
- Apply updates: Follow the WordPress update process
- Plugin/Theme updates: Manage through WordPress admin interface
Container Updates
To update the WordPress container:
# Pull latest image
docker compose pull wordpress
# Restart with new image
docker compose up -d wordpress
Database Updates
MariaDB updates:
# Pull latest MariaDB image
docker compose pull wordpress-db
# Backup first!
docker compose exec wordpress-db mysqldump -u root -p wordpress > backup.sql
# Update
docker compose up -d wordpress-db
Backups
⚠️ Always backup before updating!
Automated backups via cron
Set up a monthly backup that runs as root on the first day of each month. The
backup.sh script will create compressed backups and prune old archives based on its
retention settings.
-
Ensure the script is executable
chmod +x /srv/wordpress-sauna/backup.sh -
Install a root cron job
sudo crontab -eAdd this line to run at 00:00 on the 1st of every month, with no output:
0 0 1 * * /srv/wordpress-sauna/backup.sh >/dev/null 2>&1
Notes:
- Environment: The script sources
/srv/wordpress-sauna/.env. Ensure it exists and contains the required variables. - Working directory: The script
cds to its directory, so absolute paths in the cron entry are sufficient. - Docker: Docker and the compose stack must be running for the database dump to succeed.
Database Access
# Access MariaDB from container
docker compose exec -it wordpress-db mysql -u root -p
# Access specific database
docker compose exec -it wordpress-db mysql -u wordpress -p wordpress
Logs
# All services
docker compose logs -f
# Specific service
docker compose logs -f wordpress
docker compose logs -f wordpress-db
Troubleshooting
Common Issues
- Connection refused: Check if reverse proxy is running and connected to the same network
- Database connection errors: Verify MariaDB is running and credentials are correct
- Permission errors: Check file permissions in the
./wordpressdirectory - Update failures: Ensure proper file permissions for WordPress updates
Testing
Test your WordPress installation:
# Check if WordPress is responding
docker compose exec wordpress curl -I http://localhost
# Check database connection
docker compose exec wordpress-db mysqladmin ping -u root -p
# Check WordPress status
docker compose exec wordpress wp core version
Debugging
# Check service status
docker compose ps
# View WordPress logs
docker compose logs wordpress
# Check network connectivity
docker compose exec wordpress ping wordpress-db
Integration with Reverse Proxy
Requirements for Reverse Proxy
Your reverse proxy (nginx) should:
- Be on the same
reverse-proxynetwork - Route traffic to
wordpress:80 - Handle SSL termination
- Forward proper headers (X-Forwarded-For, X-Forwarded-Proto, etc.)
- Support PROXY protocol if behind a load balancer
Example nginx configuration
server {
listen 443 ssl proxy_protocol;
server_name yourdomain.com;
location / {
proxy_pass http://wordpress:80;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
}
}
Development vs Production
Development Setup
For development, the current configuration exposes port 8080:
ports:
- 8080:80
Access via: http://localhost:8080
Production Setup
For production, remove the port mapping:
# Remove this line for production
# ports:
# - 8080:80
Access only through the reverse proxy: https://yourdomain.com
Support
License
This setup is provided as-is. WordPress is licensed under the GPL v2 or later.