WordPress with Varnish in Docker Ubuntu/Debian
Set up WordPress with Varnish, Apache and MariaDB in Docker on your own server or VPS: add the Docker repository, start the ready-made docker-compose template, then adjust domain, mail delivery and database.
Running a current WordPress behind a Varnish cache, with Apache and MariaDB, takes only a handful of commands once Docker is involved. This guide is for anyone with their own server or VPS running Debian or Ubuntu. Shared web hosting (wordpress.com, GoDaddy and the like) is out of scope.
What you need: a user with root or sudo rights and a few minutes. Docker itself gets installed from the official repository in the first steps below.
Every command runs on the server over SSH. On Linux and macOS the built-in terminal is enough; on Windows, a client such as PuTTY does the job. Log in with ssh user@ServerIP. If you would rather use a key than a password, see Create an SSH key. You can also try everything on your own machine first and repeat it on the server later.
Start with the packages the Docker repository and the template depend on:
sudo apt update
sudo apt install ca-certificates curl git nano -y
Next, fetch Docker’s GPG key and add the repository. Docker keeps separate repositories for Debian and Ubuntu, linux/debian and linux/ubuntu (Ubuntu install guide). The commands read which one applies from /etc/os-release, along with your release’s codename. That way they run unchanged on every release Docker currently supports: Debian 12 and 13 and the Ubuntu LTS releases from 22.04 on. As in Docker’s current guide, the key ends up in /etc/apt/keyrings and the package source in a file called docker.sources.
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/$(. /etc/os-release && echo "$ID")/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/$(. /etc/os-release && echo "$ID")
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
Now install Docker. The first line clears out packages that Debian and Ubuntu ship themselves under names like docker.io or docker-compose. On Debian 13 the installation otherwise fails with a file conflict. On a fresh server the line finds nothing, and if the packages are there, apt asks before removing them.
sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc 2>/dev/null | cut -f1)
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
This way Compose comes straight from Docker as docker-compose-plugin, and the command is docker compose, with a space rather than a hyphen. Older guides, including earlier versions of this one, install the docker-compose package from the distribution instead. Debian 12 and Ubuntu 24.04 ship Compose 1.29.2 under that name, Debian 13 ships 2.26.1 (Debian sources, Launchpad). Docker’s Debian install guide lists it among the unofficial packages to remove before installing, which is why it appears in the first line above.
The actual setup lives in a Docker template on GitHub. It starts four containers: WordPress on Debian with Apache and PHP 8.5, Varnish in front of it as a cache, MariaDB and phpMyAdmin. Create a folder for all Docker data and clone the template into it. The folder sits under /var and belongs to root, so mkdir and git clone run with sudo.
Type the following into your shell:
sudo mkdir /var/docker
cd /var/docker
sudo git clone https://github.com/studplus/wordpress_docker.git
cd wordpress_docker
The wordpress_docker folder now holds docker-compose.yml plus subfolders with the settings for WordPress, Varnish and MariaDB.
A first start shows whether everything works. If this installation is going straight into production, change the database credentials in docker-compose.yml before that first start (details further down). MariaDB only applies them while its data directory is still empty, according to the MariaDB image docs on Docker Hub. Then run:
sudo docker compose up -d
On first start, Docker builds the WordPress image from the template’s Dockerfile and pulls ready-made images for Varnish, MariaDB and phpMyAdmin.
Once all containers are up, the output looks like this:
Varnish answers on port 80. Open your server’s IP address in the browser, or http://0.0.0.0 if you are testing locally. If the WordPress setup screen appears, the stack is running. WordPress itself also listens on port 8080 without the cache, and phpMyAdmin on port 8081.
To run WordPress under your own domain and send mail, edit four files in the custom-wordpress folder, then docker-compose.yml. The Dockerfile copies those four files into the image at build time, so changes only take effect after a rebuild, for example with sudo docker compose up -d --build. Like the whole folder, the files belong to root, so open them with sudo nano.
aliases decides where mail for local accounts ends up. After root:, enter a server address that tells you where a message came from. After Wordpress:, enter the public address, the one customers see. The first line forwards postmaster to that entry.
sudo nano custom-wordpress/aliases
Save and exit: press Ctrl+X, then Y and Enter
app.conf is the Apache virtual host. Set ServerAlias to your domain, e.g. mydomain.com, and ServerAdmin to your email address.
cronjob calls wp-cron.php every minute. Replace http://0.0.0.0 in it with your own address, e.g. https://mydomain.com.
msmtprc handles outgoing mail. I recommend a dedicated email service for this: a regular mailbox with IONOS, STRATO or GoDaddy works, as does an SMTP provider such as Amazon SES, SendGrid or Mailgun. Remove the hash (#) in front of host, port, user and password. Put your SMTP server after host, for example smtp.ionos.com, a port such as 587 after port, then your username and password. from holds the sender address.
docker-compose.yml: Change the database user and password and the name under MYSQL_DATABASE. Every password in the template is literally password, including MYSQL_ROOT_PASSWORD. Ports can be adjusted here too.
On a public server that matters. Besides port 80, the template publishes 8080, 8081 and the database on 3306. Docker makes published ports reachable from outside by default (port publishing), and ufw won’t stop that, because Docker reroutes the traffic before the firewall rules apply (Docker and ufw). Writing a port as 127.0.0.1:3306:3306 limits it to the server itself. To see what is actually open, check How to Show Used Ports in Linux.
WordPress setup: When you open 0.0.0.0 or your domain, the installer starts. Enter the database name, user and password exactly as they appear under db in docker-compose.yml. For the database host, db is enough: Compose registers each service name with an internal DNS server (Networking in Compose), so WordPress finds the database without an IP address.
Backing up and restoring that database is covered in MariaDB MySQL backup and restore in Docker.
Have fun!