No description
Find a file
2025-09-02 10:03:03 +02:00
.gitignore initial commit 2025-09-02 10:03:03 +02:00
backup.sh initial commit 2025-09-02 10:03:03 +02:00
compose.override.yml initial commit 2025-09-02 10:03:03 +02:00
compose.yml initial commit 2025-09-02 10:03:03 +02:00
example.env initial commit 2025-09-02 10:03:03 +02:00
README.md initial commit 2025-09-02 10:03:03 +02:00

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

  1. Clone or download this repository

    git clone <repository-url>
    cd wordpress-sauna
    
  2. Create environment file

    cp example.env .env
    
  3. 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@');
    
  4. 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@'
    
  5. Start the services

    docker compose up -d
    

    Docker Compose automatically applies compose.override.yml. This attaches the wordpress service to the external reverse-proxy network (and keeps the example port mapping commented for production friendliness).

  6. Access WordPress

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-proxy network exists (usually created by the nginx setup).
  • Prefer defining network membership in your Compose files over manual docker network connect commands.

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-proxy network 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 ./mariadb volume
  • 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!

  1. Online generator (recommended): Visit https://api.wordpress.org/secret-key/1.1/salt/

  2. WordPress CLI (after initial setup):

    docker compose exec wordpress wp config shuffle-salts
    
  3. 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:

  1. Mount a custom wp-config.php:

    volumes:
      - ./wp-config.php:/var/www/html/wp-config.php
    
  2. 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:

  1. Access WordPress admin: https://yourdomain.com/wp-admin/
  2. Check for updates: Dashboard → Updates
  3. Apply updates: Follow the WordPress update process
  4. 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.

  1. Ensure the script is executable

    chmod +x /srv/wordpress-sauna/backup.sh
    
  2. Install a root cron job

    sudo crontab -e
    

    Add 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

  1. Connection refused: Check if reverse proxy is running and connected to the same network
  2. Database connection errors: Verify MariaDB is running and credentials are correct
  3. Permission errors: Check file permissions in the ./wordpress directory
  4. 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:

  1. Be on the same reverse-proxy network
  2. Route traffic to wordpress:80
  3. Handle SSL termination
  4. Forward proper headers (X-Forwarded-For, X-Forwarded-Proto, etc.)
  5. 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.