Deep Dive on Docker Compose – Deploying WordPress
Let’s talk about running multiple Docker containers together. Specifically, I’ll show you how to spin up a WordPress site with a database backing it, using Docker Compose.
If you’ve worked with Docker before, you know you can manage individual containers through the command line. It works fine for one or two containers. But once you have several services talking to each other, running them separately gets old fast. You’d need to remember the right flags for each container, start them in the correct order, and keep track of networking between them. That’s the problem Compose tries to solve.
How Docker Compose Works
Docker Compose lets you describe an entire application stack in a single YAML file. Instead of running a long docker command with a dozen flags, you write the configuration once and let Compose handle the rest.
Here’s a quick example of what a typical application might look like:
Back end (API)
CMS (WordPress)
Web front-end
Database
Machine Learning Tasks
Logging
Reverse Proxy
Each of these would be its own container. Compose lets you define them all in one place and bring them up or tear them down together.
A Bit of History
Docker Compose started as an open-source project called Orchard, built by a company of the same name. Docker acquired Orchard in 2014 and eventually folded the tool into the official Docker ecosystem. The original version was written in Python and ran as a standalone binary. These days, docker compose (as a Docker CLI plugin) is the standard approach, though you’ll still see docker-compose (the hyphenated version) in older tutorials and documentation.
Installing Compose
If you have Docker Desktop, you’re already set. Compose comes bundled with it.
On Linux, you might need to install it separately. Grab the binary from the GitHub releases page:
curl -SL https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
Make it executable:
chmod +x /usr/local/bin/docker-compose
To verify:
docker compose version
You should see something like Docker Compose version v2.24.0 or later.
Compose Files
Compose uses YAML files to define services. The default filename is docker-compose.yml, and if you use that name, you don’t need to specify it when running commands. If you use a different name, pass it with the -f flag.
The file structure has a few key sections at the root level:
version
services
volumes
The version key refers to the Compose file format itself, not to Docker Compose or Docker Engine versions. Check the official documentation if you need to verify compatibility between file format versions and your Docker installation.
Deploying WordPress with Docker Compose
Here’s a complete example. Create a file called docker-compose.yml with this content:
version: '3.9'
services:
wordpress:
image: wordpress
restart: always
ports:
- 80:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: exampleuser
WORDPRESS_DB_PASSWORD: examplepass
WORDPRESS_DB_NAME: exampledb
volumes:
- ./volumes/wp:/var/www/html
db:
image: mysql:8.0
restart: always
environment:
MYSQL_DATABASE: exampledb
MYSQL_USER: exampleuser
MYSQL_PASSWORD: examplepass
MYSQL_ROOT_PASSWORD: '1'
volumes:
- ./volumes/db:/var/lib/mysql
Then create the directories for persistent storage:
mkdir -p volumes/wp volumes/db
A few things to notice here. The services section defines each container. I called them wordpress and db, but you can name them whatever makes sense. Those names become part of the container names, so Docker Compose will create containers called something like app_wordpress_1 and app_db_1 (the prefix comes from your project directory name).
The wordpress service uses the official WordPress image, which already includes Apache and PHP. The db service runs MySQL. Both have restart: always set, which means Docker will restart them if they crash or if you reboot the host.
Data persistence works through the volumes section. Even if you delete the containers, the data in ./volumes/wp and ./volumes/db stays on your host. This matters when you update container versions or recreate them.
Starting WordPress
From the directory where you saved docker-compose.yml, run:
docker compose up -d
The -d flag runs everything in the background so you get your terminal back.
Give it a minute. WordPress needs to initialize its database and start Apache. Once it’s ready, open your browser and go to http://localhost:80. You should see the WordPress setup screen.
Checking What’s Running
To see your active containers:
docker ps
You’ll see something like:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
45eb5c635989 wordpress "docker-entrypoint.s…" 3 weeks ago Up 5 days 0.0.0.0:80->80/tcp, :::80->80/tcp app_wordpress_1
9c2028941a97 mysql:8.0 "docker-entrypoint.s…" 3 weeks ago Up 5 days 3306/tcp, 33060/tcp app_db_1
To see processes running inside the containers:
docker compose top
This shows PID numbers from the Docker host perspective, not from inside the containers.
Managing the Stack
Here are the commands you’ll use most often.
Stop everything without deleting containers:
docker compose stop
Containers stop, but volumes and images stay. You can start again with docker compose start or docker compose restart.
Remove stopped containers and networks:
docker compose rm
This doesn’t touch volumes or images.
Stop and remove everything (containers, networks), but keep volumes:
docker compose down
To also remove volumes (this deletes your data):
docker compose down -v
Restart after changes:
docker compose restart
If you’ve edited the Compose file and want to apply those changes, use docker compose up -d again. For new containers or changes to image versions, you may need to run docker compose down followed by docker compose up -d.
What About the Port Mapping?
The ports section maps host ports to container ports. In our example, - 80:80 means traffic sent to port 80 on your host goes to port 80 inside the container. WordPress inside the container listens on port 80, so this makes sense.
If port 80 on your host was already in use, you could change it to something like - 8080:80 and access WordPress at http://localhost:8080.
A Note on MySQL Versions
This post originally used mysql:5.7. That version reached end of life in October 2023, so I switched the example to mysql:8.0 which is current. If you’re working with older applications that need MySQL 5.7, you can still use the old image, but be aware it’s not getting security updates anymore.
Where Docker Stores Data by Default
When you don’t specify a host path for a volume, Docker stores it in the default location. On Linux, that’s /var/lib/docker/volumes. Each named volume gets its own folder there. You can check with:
docker volume ls
Final Thoughts
Docker Compose isn’t complicated once you get past the YAML structure. The workflow I’ve described here covers the basics: defining services, persisting data, and managing the application lifecycle.
The real value shows up when you start adding more services. A typical web application might have a reverse proxy, a caching layer, a database, and multiple backend services. With all of them in one Compose file, you can bring everything up with a single command and know the dependencies are handled correctly.
Keep your Compose files in version control. They’re a form of documentation, and they make it much easier to recreate your environment on another machine or for a new team member.
Comments