Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsInstall WordPress on a VPS as two Docker Compose services: a WordPress container and a MariaDB database container, with named volumes mounted at /var/www/html and /var/lib/mysql. Keep the database private, bind WordPress to localhost, and let a reverse proxy provide the public HTTPS endpoint. This layout survives container replacement, supports repeatable updates, and gives you clear backup and rollback boundaries.
Antihost offers managed WordPress hosting with free migration, one-click staging and a 14-day free trial.
What you are building
The finished deployment has four layers:
- Docker Engine and Compose: the runtime installed on the VPS.
- WordPress: the web application and its themes, plugins, uploads, and configuration.
- MariaDB: the database WordPress uses for posts, settings, users, and plugin data.
- Reverse proxy and DNS: the public edge that accepts HTTP/HTTPS traffic, obtains a TLS certificate, and forwards requests to WordPress.
Compose gives the containers a private network. WordPress connects to the database at db:3306, where db is the Compose service name. You do not need to publish the database port on the VPS. Named volumes keep the database and WordPress tree when containers are recreated; deleting those volumes is a separate, destructive operation.
As an Amazon Associate I earn from qualifying purchases.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallPrerequisites and sizing decisions
Supported VPS and operating-system checks
Use a supported 64-bit Linux VPS with administrative SSH access. Docker currently lists Ubuntu 22.04, 24.04, and 26.04 among its supported Ubuntu releases. Confirm the exact distribution and release before running installation commands:
cat /etc/os-release
uname -m
The VPS needs space for the operating system, Docker images, the database, WordPress media, backups, and container logs. There is no authoritative minimum disk size for every WordPress workload: a small text-only site and a media-heavy site have very different requirements. Check free space with df -h and plan a separate location for backups.
#1 Best Overall
DNS and access
- Create a DNS A record for the domain pointing to the VPS IPv4 address. Add an AAAA record only when the VPS has working IPv6 and the proxy is configured for it.
- Keep an administrative SSH account available, preferably with key authentication.
- Decide which reverse proxy will terminate TLS. Nginx, Caddy, Traefik, or another trusted proxy can forward the domain to the local WordPress listener.
- Allow only the public services you actually operate. A typical web server exposes SSH, HTTP, and HTTPS; the database should remain private.
Install Docker Engine and the Compose plugin
Use Docker’s current repository instructions for the VPS distribution rather than installing the legacy standalone Compose binary. On Ubuntu, the documented package set includes Docker Engine, the Docker CLI, containerd, Buildx, and the Compose CLI plugin.
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl status docker
sudo docker run hello-world
sudo docker compose version
The final command should print the installed Compose plugin version. If Docker is not active, inspect sudo systemctl status docker and the service journal before continuing. Docker warns that published container ports can bypass rules written only for ufw or firewalld, so review the effective firewall policy after the stack is running rather than assuming a host rule protects every published port.
Recommended Free Tools
Create a private Compose project
Make the project directory
mkdir -p ~/wordpress
cd ~/wordpress
Keep compose.yaml, the environment file, and deployment notes in this directory. Store the directory in version control if appropriate, but never commit the file containing live passwords.
Create the environment file
Create .env with unique, long random values. The two passwords should be different: one is used by the WordPress database account and the other by the MariaDB root account.
MYSQL_DATABASE=wordpress
MYSQL_USER=wordpress
MYSQL_PASSWORD=replace-with-a-long-random-password
MYSQL_ROOT_PASSWORD=replace-with-a-different-long-random-password
Restrict the file to the deployment account:
chmod 600 .env
Do not put these credentials in a public repository, a shell history entry, or a support ticket. Compose reads the variables when it processes compose.yaml; changing them later does not automatically change an already-initialized database account.
Create compose.yaml
This Apache-based example binds WordPress to loopback on port 8080. The proxy on the VPS can reach it, while an external client cannot connect to that container port directly.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →services:
db:
image: mariadb:10.6
restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- db_data:/var/lib/mysql
wordpress:
image: wordpress:apache
restart: unless-stopped
depends_on:
- db
ports:
- "127.0.0.1:8080:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: ${MYSQL_USER}
WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
volumes:
- wp_data:/var/www/html
volumes:
db_data:
wp_data:
The important boundaries are the service name, the private port binding, and the volume mounts. WordPress uses db:3306, not localhost and not a public VPS address. The database volume is mounted at /var/lib/mysql; the WordPress volume is mounted at /var/www/html.
Rank #2
The sample pins MariaDB to the 10.6 tag and uses the Apache WordPress image. Select an exact database tag after checking compatibility with your plugins and existing data, then record that choice in version control. The official Compose example also demonstrates a MySQL 8.0.27 option; neither database family is universally superior. Compatibility, lifecycle support, migration tooling, and your team’s operating experience should decide the choice.
Start the containers and complete WordPress setup
- Pull the images.
docker compose pull - Start the project in the background.
docker compose up -d - Confirm both services are running.
docker compose ps - Inspect the initial application output.
docker compose logs --tail=100 wordpress
On the first launch, MariaDB initializes its data directory and WordPress prepares its files. The official image documentation cautions that initialization can take time; a blank or incomplete first response is not proof that the deployment failed. Wait briefly, then check both service logs.
For a temporary direct test, you can change the port mapping to 8080:80 and request http://VPS_IP:8080. This exposes the port on every interface, so use it only for a controlled test and remove the public binding afterward. The safer production mapping is the loopback binding shown above.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Once the proxy is forwarding the final domain, open the WordPress installer through that domain. Set the site address to the final https:// URL during setup. If WordPress is first configured with a localhost or temporary IP address, generated links and redirects can continue using that address until the site URL is corrected.
Put the site behind DNS, HTTPS, and a reverse proxy
Point the domain to the VPS
Publish the A record, wait for the record to resolve, and verify that it reaches the intended server. If you use IPv6, verify the AAAA path as well; an incorrect AAAA record can send some visitors to a different machine even when IPv4 is correct.
Forward the domain to WordPress
Configure the chosen reverse proxy to:
- accept the domain on ports 80 and 443;
- redirect plain HTTP to HTTPS;
- proxy HTTPS requests to
http://127.0.0.1:8080; - request and renew a certificate through the proxy’s supported ACME workflow;
- pass the original host and scheme so WordPress can generate correct absolute URLs.
The proxy, not the database container, is the public edge. Keep the Compose port bound to loopback and do not add a database port mapping such as 3306:3306.
If you choose the FPM image
An FPM-based WordPress image is a different architecture from the Apache example. FPM speaks FastCGI, not HTTP, so it must sit behind a FastCGI-capable reverse proxy. Directly exposing FPM is inherently unsafe because the protocol is not intended to be a public listener.
Rank #3
The official image guidance also requires proxy configuration changes for FPM: remove the usual file-existence instruction and adjust the SCRIPT_FILENAME path from /var/www/html to /usr/src/wordpress. Do not copy an Apache proxy configuration unchanged to an FPM deployment.
| Choice | What the proxy must do | Operational trade-off |
|---|---|---|
wordpress:apache |
Forward ordinary HTTP to the container’s port 80 | Simpler first deployment and fewer moving parts |
| FPM-based WordPress image | Use FastCGI and the image’s required script path | Can fit an existing FastCGI architecture, but misconfiguration or direct exposure is dangerous |
Secure the VPS and the Compose project
- Protect secrets: replace every sample password, keep
.envmode 600, and limit shell and filesystem access to trusted administrators. - Keep the database private: WordPress reaches
db:3306on the Compose network; no host port is required. - Open only necessary firewall ports: normally SSH, HTTP, and HTTPS. Reconcile host firewall rules with Docker’s warning that published ports may bypass ufw or firewalld filtering.
- Use the final HTTPS URL: this avoids mixed-content links, incorrect redirects, and cookies scoped to a temporary address.
- Review image tags: floating tags make updates convenient but reduce reproducibility. Pin tags deliberately, record changes, and read release notes before deployment.
- Control log growth: Docker’s default
json-filelogging can grow until it consumes the disk. Configure rotation or another logging driver before a busy site fills its volume.
Understand persistence before replacing anything
The named volumes are the persistence boundary:
| Volume | Container path | What it contains |
|---|---|---|
db_data |
/var/lib/mysql |
MariaDB’s database files |
wp_data |
/var/www/html |
WordPress core files, uploads, themes, plugins, and generated files |
Running docker compose down removes the containers and their network but preserves both volumes. Running docker compose down --volumes also deletes the volumes and therefore the database and WordPress tree. Treat the latter as destructive; do not use it as a routine way to restart the application.
Compose may prefix volume names with the project name. If you need to copy a volume, check the actual name first:
docker volume ls
Back up the database and WordPress files
A usable backup includes both the logical database and the WordPress files. Backing up only one side can leave you with posts but no uploads, or uploads without the database records that reference them. Store backup files outside the live volumes and outside the VPS when possible.
Free tools Windows power users keep installed
One-click scans. No signup required.
Database dump
Load the Compose variables into the current shell without printing them, then write a logical dump:
set -a
. ./.env
set +a
mkdir -p backups
docker compose exec -T db mariadb-dump -u root -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" > backups/wordpress.sql
The dump command reads the root password from the environment variable in your shell. Protect the resulting SQL file just as carefully as the live database credentials.
Archive the WordPress volume
Stop writes briefly if you need a consistent file snapshot, then archive the volume to the project backup directory. Replace wordpress_wp_data with the actual name shown by docker volume ls if Compose used a different project prefix.
Rank #4
docker run --rm
-v wordpress_wp_data:/data
-v "$PWD/backups":/backup
alpine tar czf /backup/wp_data.tgz -C /data .
Copy backups/wordpress.sql and backups/wp_data.tgz to storage that is independent of the VPS. A backup that remains on the same disk as the live site does not protect against disk loss or a compromised server.
Rehearse a restore
Do not trust a backup merely because the command completed. Restore it into an isolated Compose project or a disposable VPS:
- Create fresh volumes and start a database container with compatible credentials and the same database family.
- Import the SQL dump with the database client inside the database container.
- Extract the WordPress archive into the new WordPress volume while preserving the expected directory tree.
- Start WordPress and verify that the administrator login, posts, media, themes, plugins, and site URLs work.
- Record how long the restore took and which image tags and environment values were required.
Docker’s containerization guidance calls for backing up the WordPress files and MySQL database before moving an existing site. The same discipline applies before upgrades that could alter the database schema.
Update the stack without losing data
Updates are an operational task, not merely a new image download. Pin the image tags in your Compose file or deployment repository, review the release notes, and keep a tested rollback path and current database backup.
- Check the current state and recent errors:
docker compose ps docker compose logs --tail=100 db wordpress - Back up the database and WordPress volume.
- Change the image tag deliberately and commit the change if you keep the project under version control.
- Pull the selected images:
docker compose pull - Recreate the services:
docker compose up -d - Watch startup output and test the public HTTPS site, administrator login, media uploads, and critical plugins.
Recreating containers does not remove named volumes, so the data remains available to the replacement containers. Never combine a routine update with docker compose down --volumes. If a new image fails, restore the previous image tag and use the database backup if a schema change cannot be reversed safely.
Rebuild and redeploy regularly for current security updates, but schedule the work so you can observe logs and restore service if a plugin or extension is incompatible.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Troubleshoot common failures
WordPress reports that it cannot connect to the database
- Check that
WORDPRESS_DB_HOSTis exactlydb:3306;localhostpoints to the WordPress container itself, not the MariaDB service. - Compare
MYSQL_DATABASE,MYSQL_USER, andMYSQL_PASSWORDin.envwith the WordPress environment values. - Inspect both logs:
docker compose logs db wordpress. - On a first start, wait for MariaDB initialization to finish before judging the result.
- Remember that changing credentials in
.envdoes not rewrite an already-initialized database. Change the database account deliberately or initialize a new test volume.
The first page is blank or incomplete
Wait for initialization and inspect the WordPress and database logs. Check that the reverse proxy is forwarding to 127.0.0.1:8080, that DNS resolves to this VPS, and that the proxy is not serving a stale certificate or upstream error page.
Best Value
The domain redirects to localhost or an old IP
Complete the web installer through the final HTTPS domain, then check WordPress’s site URL settings. Also inspect proxy headers and any cached redirect at the browser or edge layer. A temporary direct test address should not become the canonical site URL.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →A plugin needs a PHP extension
The official WordPress image does not include every possible PHP library. Build a derived image that installs the required extension, pin the resulting base image and extension versions, and test the plugin before deploying it to the live site. Do not install ad-hoc packages inside a running container and expect them to survive recreation.
The VPS disk keeps filling
- Check Docker images, containers, volumes, and logs with the Docker inspection commands appropriate to your distribution.
- Configure rotation for the default
json-filelogs or select a logging driver with retention limits. - Move backups off the server and prune only unused resources after confirming that no rollback image or volume is needed.
- Monitor the database and media volume separately; deleting a volume to reclaim space destroys the site data.
Apache versus FPM, and MariaDB versus MySQL
The Compose pattern is the same, but the operational decisions differ.
| Decision | Use the simpler path when | Choose the other path when |
|---|---|---|
| Apache image versus FPM image | You want an HTTP-speaking container and a straightforward proxy configuration. | You already operate a FastCGI-capable edge, understand the required script path, and need that architecture’s tuning or caching model. |
| MariaDB versus MySQL | Your existing plugins, exports, and operational procedures are tested with that database family. | You have a compatibility or support requirement for the other family and have tested import, upgrades, and backups. |
The official sample demonstrates both MariaDB and MySQL families but does not establish a universal winner. Validate the exact image tag, WordPress version, and plugin requirements together before production.
Production launch checklist
- Docker Engine, CLI, Buildx, and the Compose plugin are installed from the distribution-appropriate Docker repository.
docker compose version,docker compose ps, and the service logs show a healthy startup.- The database has no published host port and is reachable from WordPress as
db:3306. db_dataandwp_dataare mounted at the intended paths.- The domain’s A and, if applicable, AAAA records point to the VPS.
- The reverse proxy redirects HTTP to HTTPS and forwards to the loopback WordPress listener.
- The WordPress site URL uses the final HTTPS domain.
- Firewall rules allow only required services, with Docker’s published-port behavior accounted for.
.envis private, sample passwords are gone, and the file is excluded from source control.- Database and file backups exist off the VPS, and a restore rehearsal has succeeded.
- Image tags, release notes, rollback steps, and a maintenance window are documented.
- Container log rotation is configured so logs cannot consume the disk indefinitely.
How the lifecycle behaves
When you run docker compose up -d, Compose creates a private network, starts MariaDB and WordPress, and attaches the named volumes. When you replace a container during an update, the new container mounts the same volume and sees the existing database and WordPress files. When you run docker compose down, the containers and network disappear but the volumes remain. Only an explicit volume deletion, such as docker compose down --volumes, removes that persistent data.
That separation is the reason this deployment is maintainable: container images can be replaced, while application state is backed up and retained independently. Treat the volumes, credentials, proxy configuration, and restore procedure as one production system rather than as disposable development files.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




