How to Install WordPress with Docker Compose
How to install WordPress with Docker Compose: MariaDB and WordPress containers, passwords in .env, Nginx and SSL, upload limits, backups and updates.

Contents
Installing WordPress with Docker Compose means running WordPress and its database as two containers defined in a single file. You don't need to install PHP, a web server or a database on the server; everything comes from official images. You can run several sites on the same server, isolated from each other, and move a site to a new server by copying its folder and data.
This guide covers installing WordPress and MariaDB with Docker Compose, storing passwords securely, putting Nginx and SSL in front, upload limits, backups and updates. If Docker isn't installed, follow our Docker installation guide first.
Folder structure
Create a folder for the site:
mkdir -p ~/sites/yourdomain && cd ~/sites/yourdomain
This folder will hold three files: compose.yaml, .env and uploads.ini.
Step 1: Put the passwords in the .env file
Instead of writing passwords directly into the compose file, keep them in a separate .env file:
DB_PASSWORD=a-long-random-password
DB_ROOT_PASSWORD=another-long-random-password
You can use the openssl rand -base64 24 command to generate random passwords. Protect the file so that only you can read it: chmod 600 .env. If you keep this folder in a Git repository, add the .env file to .gitignore.
Step 2: compose.yaml
services:
db:
image: mariadb:11
restart: unless-stopped
environment:
MARIADB_DATABASE: wordpress
MARIADB_USER: wordpress
MARIADB_PASSWORD: ${DB_PASSWORD}
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
volumes:
- db:/var/lib/mysql
wordpress:
image: wordpress:php8.3-apache
restart: unless-stopped
depends_on:
- db
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
WORDPRESS_CONFIG_EXTRA: |
if (isset($$_SERVER['HTTP_X_FORWARDED_PROTO']) && $$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$$_SERVER['HTTPS'] = 'on';
}
ports:
- '127.0.0.1:8080:80'
volumes:
- wp:/var/www/html
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro
volumes:
db:
wp:
- db: The MariaDB database. The data is kept in the
dbvolume; it isn't lost even if the container is deleted. - wordpress: The official WordPress image (PHP and Apache included). WordPress files, uploads and plugins are kept in the
wpvolume. 127.0.0.1:8080:80: Exposes WordPress only to the server itself. Outside access will go through Nginx in front of it. We explain in our Docker installation guide that ports published by Docker can bypass the firewall.WORDPRESS_CONFIG_EXTRA: Lets WordPress correctly detect when the Nginx in front of it uses HTTPS; otherwise you can run into redirect loops. In the compose file, the$character is written as$$.- The database port isn't published. WordPress reaches the database by the name
dbover Docker's internal network; the database is never exposed to the outside.
Step 3: Upload limits
The uploads.ini file sets PHP's upload and memory limits:
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
max_execution_time = 120
We explain what these settings do in our How to change the PHP version guide.
Step 4: Start it
docker compose up -d
docker compose ps
Both containers should be in the running state. On the first start, preparing the database takes a few seconds.
Step 5: Nginx and SSL
Put Nginx on the server in front of the WordPress container as a reverse proxy. /etc/nginx/sites-available/yourdomain.com:
server {
listen 80;
listen [::]:80;
server_name yourdomain.com www.yourdomain.com;
client_max_body_size 64M;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Enable the site and get SSL with Certbot:
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
client_max_body_size should match the upload limit in the uploads.ini file; otherwise large uploads stop at Nginx with a 413 error. Then open https://yourdomain.com and complete the WordPress setup wizard. We cover the details of Nginx and SSL in our Nginx installation guide.
Backups
A backup of a WordPress site has two parts: the database and the files.
Database:
docker compose exec -T db sh -c 'mariadb-dump -uroot -p"$MARIADB_ROOT_PASSWORD" wordpress' > db-$(date +%F).sql
Files (the wp volume):
docker run --rm -v yourdomain_wp:/data -v "$(pwd)":/backup alpine \
tar -czf /backup/wp-$(date +%F).tar.gz -C /data .
Volume names start with your folder name (like yourdomain_wp); you can see the full name with docker volume ls. We cover automating backups with cron and keeping them somewhere independent of the server in our Server backups guide.
Updates
- WordPress, plugins and themes: Because the files are kept in a volume, they're updated from the admin panel as usual.
- Images (PHP, Apache, MariaDB): Update the images regularly to get system packages and security fixes:
docker compose pull docker compose up -d
Updating the image doesn't overwrite your WordPress files. Always take a database backup before changing MariaDB's major version.
Multiple sites
Create a separate compose.yaml in a separate folder for each site and give each one a different local port (127.0.0.1:8081:80, 127.0.0.1:8082:80 ...). Nginx in front of them routes each domain name to the right port. Giving each site its own database container keeps a problem on one site from affecting the others.
WordPress with Docker at Nodesty
Nodesty AMD Ryzen 9 9950X VPS plans render PHP pages quickly thanks to their high single-core speed; with full root access, you can run Docker, Nginx and several WordPress sites on the same server. NVMe storage, nShield DDoS protection and free daily backups kept for 7 days are included in the plan. If you only want to publish a WordPress site without managing a server, our web hosting plans with LiteSpeed are a more practical choice; we cover the setup in our WordPress installation guide.
Frequently Asked Questions
Docker or web hosting for WordPress?
If you only want to publish a WordPress site, web hosting is easier; server management, updates and security are the provider's job. If you want to run several sites or other services on the same server, control the environment completely and move sites easily, Docker gives you flexibility.
Do I need to replace the container when I update WordPress?
Because the WordPress files are kept in a volume, you update the core, plugins and themes from the admin panel as usual. Updating the image refreshes the PHP version and system packages; it doesn't overwrite your WordPress files.
Can I run several WordPress sites on the same server?
Yes. Create a separate folder and compose.yaml file for each site and give each one a different local port. Nginx in front of them routes each request to the right container based on the domain name. Giving each site its own database container keeps the sites isolated from each other.
I get the Error establishing a database connection message. Why?
The WordPress container can't connect to the database. Check that the database container is running with docker compose ps, make sure the passwords in the .env file are the same for both containers, and on the first setup wait a few seconds for the database to be ready.
I changed the password in the .env file but it doesn't work. Why?
The MariaDB image sets the user and password only when the database is first created. If the database volume already exists, a change in the .env file doesn't affect the existing user. You need to change the password from inside the database, or delete the volume on a fresh installation.
Where are my uploaded files and plugins stored?
All of WordPress's files, including uploads and plugins, are stored in the wp volume defined in the compose file. Even if you delete and recreate the container, your files aren't lost as long as the volume stays in place.
About the author
Nodesty Team · Infrastructure & Support
The Nodesty team runs our servers, network and DDoS protection day to day and answers customer support requests around the clock. These guides come from that work.
Related articles

Linux Timezone and NTP: A Server Time Setup Guide
How to set the timezone and NTP on a Linux server: timedatectl, UTC or local time, chrony and timesyncd, cron, logs, 2FA codes and Windows Server.

How to Install Ollama: Run AI Models on Your Own Server
How to install Ollama and run AI models on your own server: setup, choosing a model and RAM, using the API, Open WebUI, secure access and CPU usage.

Server Monitoring: Tracking Resources, Services and Alerts
How do you monitor a server? What to watch, real-time monitoring with Netdata, external checks with Uptime Kuma, Prometheus and Grafana, alerts and logs.