What is WordPress?
WordPress is the most widely used content management system on the web, powering blogs, business sites, shops (with WooCommerce) and membership sites. It is free and open source under the GPL, with tens of thousands of plugins and themes. The official wordpress Docker image bundles WordPress with PHP and Apache, so a full site is two containers: WordPress and a MySQL database.
Requirements
- A Linux server with Docker Engine and Docker Compose v2. A small site runs on 1 vCPU and 1 GB of RAM; give it 2 GB if you use WooCommerce or page builders.
- A domain with an A record pointing at the server.
- Basic comfort with SSH.
Step 1: Prepare the server
This guide assumes Ubuntu 24.04 with Docker installed from the official Docker Engine guide.
mkdir -p ~/wordpress && cd ~/wordpress
Create uploads.ini to raise PHP's small default upload limit:
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
Step 2: Create the Docker Compose file
This is based on the example in the official image's documentation, with secrets moved to .env, a pinned version and the database kept off the public network. Save it as docker-compose.yml:
services:
wordpress:
image: wordpress:7.1-apache
restart: unless-stopped
ports:
- "127.0.0.1:8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress:/var/www/html
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro
depends_on:
- db
db:
image: mysql:8.4
restart: unless-stopped
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_RANDOM_ROOT_PASSWORD: "1"
volumes:
- db:/var/lib/mysql
volumes:
wordpress:
db:
Create .env next to it:
DB_PASSWORD=CHANGE_ME
Replace CHANGE_ME with the output of openssl rand -hex 32. The database has no ports: entry, so it is reachable only from the WordPress container. The 7.1-apache tag receives WordPress 7.1 patch releases; WordPress core can also update itself from the dashboard, and those files persist in the wordpress volume.
Step 3: Start and open the app
docker compose up -d
docker compose logs -f wordpress
WordPress binds to localhost here, so set up HTTPS in Step 4 before running the installer. Running the installer on the final https:// domain means WordPress stores the right site URL from the start, and nobody else can reach the installer first and claim your site.
Step 4: Put it behind HTTPS
With Caddy on the host:
example.com, www.example.com {
reverse_proxy 127.0.0.1:8080
}
Caddy gets Let's Encrypt certificates for both names. The official image's wp-config.php already honours the X-Forwarded-Proto header, so WordPress knows visitors are on HTTPS and you avoid redirect loops.
Now open https://example.com, choose a language, and fill in the site title, admin username, a strong password and your email. The localhost binding matters: Docker-published ports bypass ufw, so "8080:80" would leave the site reachable over plain HTTP on port 8080 even with the firewall on.
Backups and upgrades
Back up both the database and the files (themes, plugins, uploads):
docker compose exec -T db sh -c 'exec mysqldump -u wordpress -p"$MYSQL_PASSWORD" wordpress' > wp-db-$(date +%F).sql
docker run --rm -v wordpress_wordpress:/data -v "$PWD":/backup alpine \
tar czf /backup/wp-files-$(date +%F).tgz -C /data .
Copy both files off the server. To move to a new WordPress image (for example a new PHP version), update the tag and run:
docker compose pull && docker compose up -d
Keep plugins and themes updated from the dashboard; outdated plugins are the most common way WordPress sites get hacked.
Troubleshooting
- "Error establishing a database connection": the database is still initialising on first start, or
DB_PASSWORDchanged after the database was created. The password only applies on first initialisation. - Redirect loop after enabling HTTPS: the proxy is not sending
X-Forwarded-Proto, or the site URL in Settings, General still useshttp://. - "The uploaded file exceeds the upload_max_filesize directive": check that
uploads.iniis mounted and restart the container. - Plugin installs ask for FTP credentials: file ownership in the volume is wrong. Run
docker compose exec wordpress chown -R www-data:www-data /var/www/html.
Next steps
Install a caching plugin, enable automatic minor updates for plugins, add a security plugin with login rate limiting, set up outgoing email through an SMTP plugin, and schedule the backup commands above with cron.
Spotted something out of date? Tell us and we will update the guide.