Free tools Windows power users keep installed
One-click scans. No signup required.
Run WordPress and its database as two separate Docker Compose services, keep both data sets in persistent volumes, and put HTTPS termination in a reverse proxy. The VPS supplies Docker, storage, networking, DNS and backups; the official WordPress image supplies PHP and a web server. A successful docker compose up is only the first deployment step: production also requires secret management, updates, monitoring and tested recovery.
Antihost offers managed WordPress hosting with free migration, one-click staging and a 14-day free trial.
This guide uses the official Apache image with MySQL 8.0, a layout also compatible with a supported MariaDB release. It targets a Linux VPS and current WordPress requirements published by WordPress.org in 2026.
As an Amazon Associate I earn from qualifying purchases.
What the deployment looks like
The public request path should be:
- A DNS A or AAAA record points your domain to the VPS.
- A reverse proxy such as NGINX or Apache accepts ports 80 and 443, obtains and renews the TLS certificate, and redirects HTTP to HTTPS.
- The proxy forwards application traffic to the WordPress container on a private Docker network.
- The WordPress container connects to the database by the Compose service name
db; no database port is exposed to the internet.
Compose creates a private network for the project automatically. The WordPress service stores its application tree at /var/www/html; the database service stores MySQL files at /var/lib/mysql. Named volumes preserve both locations when containers are replaced.
Prerequisites and sizing
WordPress.org’s 2026 requirements page recommends PHP 8.3 or newer, MariaDB 10.11 or newer or MySQL 8.0 or newer, and HTTPS. Apache or NGINX is recommended as the most robust and featureful server choice. The container includes PHP and its web server, but those requirements still determine the image and database versions you select.
#1 Best Overall
VPS checklist
- A supported Linux distribution with a current Docker Engine and the Docker Compose plugin.
- A static public IPv4 address and, if used, an IPv6 address.
- DNS control for the domain and firewall access to ports 80 and 443. Keep SSH restricted to your administration network where possible.
- Enough CPU, RAM, disk capacity and disk IOPS for visitors, PHP workers, media uploads, plugins, database growth and backups. The image size is not a sizing recommendation.
- Separate backup storage, ideally outside the VPS, with encryption, retention and restricted access.
Create a secure Compose project
Make a directory for the deployment and keep it outside any public web root. The example below uses Docker secrets backed by files on the VPS. It does not publish WordPress or MySQL ports; a proxy attached to the same Docker network can reach the WordPress service on port 80.
mkdir -p /opt/wpstack/secrets
cd /opt/wpstack
umask 077
printf '%s' 'replace-with-a-long-random-password' > secrets/wp_db_password
Save this as /opt/wpstack/compose.yml:
services:
wordpress:
image: wordpress:apache
restart: always
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/wp_db_password
WORDPRESS_DB_NAME: wordpress
secrets:
- wp_db_password
volumes:
- wordpress:/var/www/html
depends_on:
- db
db:
image: mysql:8.0
restart: always
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD_FILE: /run/secrets/wp_db_password
MYSQL_RANDOM_ROOT_PASSWORD: '1'
secrets:
- wp_db_password
volumes:
- db:/var/lib/mysql
secrets:
wp_db_password:
file: ./secrets/wp_db_password
volumes:
wordpress:
db:
The official image supports _FILE variables such as WORDPRESS_DB_PASSWORD_FILE and reads the value from the mounted secret. Keep the secret file out of version control and restrict its permissions. A protected .env file is another option for deployment-time values, but it must receive the same access controls.
Why these two services are separate
WordPress and MySQL have different lifecycles and security boundaries. The application reaches MySQL through the private hostname db, so the database does not need a public listener. Replacing the WordPress container does not replace the database container, and named volumes keep data independent from either container’s writable layer.
Start WordPress on the VPS
- Install Docker Engine and the Compose plugin using your distribution’s supported instructions.
- Create the project directory, secret file and Compose file shown above.
- Point the domain’s A or AAAA record at the VPS. DNS changes may take time to propagate.
- From the project directory, download the selected images:
docker compose pull - Start the services in the background:
docker compose up -d - Check container state and recent logs:
docker compose ps docker compose logs --tail=100 wordpress db - Wait for database initialization to finish, then open the domain and complete WordPress’s administrator setup. The Docker Compose sample warns that the first page load can take a couple of minutes while initialization completes.
If the site is not yet behind a proxy, do not expose the database. For a temporary local test only, bind WordPress to loopback (for example 127.0.0.1:8080:80) and remove that mapping when the proxy is ready; never publish MySQL’s port publicly.
Add HTTPS with a reverse proxy
Terminate TLS at NGINX, Apache or another reverse proxy. The proxy should be the only service with public HTTP and HTTPS ports. Obtain a certificate for the domain, configure automatic renewal, and redirect every HTTP request to HTTPS.
Proxy headers and upstream protocol
When TLS is terminated before the WordPress container, the proxy must tell WordPress that the original request was HTTPS. Set X-Forwarded-Proto to the incoming scheme and forward the original host and client address. The official image specifically requires X-Forwarded-Proto to be set appropriately when NGINX performs TLS termination.
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://wordpress:80;
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;
}
}
The hostname wordpress works when the proxy itself is a Compose service on the project’s network. If NGINX or Apache runs directly on the host, bind the WordPress port only to a loopback address and proxy to that local port, or attach the host proxy to the Docker network using an approach supported by your operating system. Do not expose the FPM or database ports to the public interface.
Where WordPress and MySQL data live
| Volume | Container path | What it contains | What happens if it is deleted |
|---|---|---|---|
wordpress |
/var/www/html |
WordPress core, generated configuration, uploads, themes and plugins | The application and media disappear; the database alone cannot recreate them |
db |
/var/lib/mysql |
MySQL or MariaDB database files | Posts, settings, users and other database content disappear |
List the volumes before maintenance:
docker volume ls
Compose normally prefixes names with the project name, so inspect the exact names and mount points with docker volume inspect. Do not treat a container’s writable layer as storage; it is lost when that container is removed.
Back up both data sets
A restorable backup has two parts: a consistent database export and a file-level copy of the WordPress volume. The database dump contains posts, pages, users, settings and plugin data, but not uploaded media. A file-only backup contains uploads, themes, plugins and configuration, but not current database content.
Database dump
Run a dump from the database container, writing the result to a protected backup directory on the VPS:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
mkdir -p /opt/wpstack/backups
chmod 700 /opt/wpstack/backups
docker compose exec -T db sh -c 'exec mysqldump -u"$MYSQL_USER" -p"$(cat /run/secrets/wp_db_password)" "$MYSQL_DATABASE"' > /opt/wpstack/backups/wordpress-$(date +%F).sql
chmod 600 /opt/wpstack/backups/wordpress-*.sql
For a large or busy site, schedule a maintenance window or use a backup method appropriate to your database’s consistency requirements. Keep multiple dated dumps rather than overwriting the only copy.
WordPress files
Identify the actual Compose volume name with docker volume ls, then archive it read-only. If your project directory is named wpstack, the volume is commonly wpstack_wordpress:
docker run --rm -v wpstack_wordpress:/data:ro -v /opt/wpstack/backups:/backup alpine tar czf /backup/wordpress-files-$(date +%F).tar.gz -C /data .
Replace the volume name with the one shown on your VPS. Copy database dumps and archives off the VPS, encrypt them, restrict who can read them, define a retention period, and periodically perform a restore test on an isolated deployment.
Restore procedure
- Stop writes or place the site in maintenance mode so the restored files and database represent the same point in time.
- Restore the file archive into the WordPress volume, preserving ownership and permissions expected by the image.
- Start the database service and import the SQL dump:
cat /opt/wpstack/backups/wordpress-YYYY-MM-DD.sql | docker compose exec -T db sh -c 'exec mysql -u"$MYSQL_USER" -p"$(cat /run/secrets/wp_db_password)" "$MYSQL_DATABASE"' - Start or restart WordPress, review logs, test the front end and administrator login, and verify media, permalinks and plugins.
Never use docker compose down --volumes for routine maintenance. That command removes named volumes, including the database volume. docker compose down removes containers and the network while preserving named volumes.
Update WordPress, images and plugins safely
The default official image enables WordPress automatic updates in the persistent /var/www/html volume. Automatic updates can be convenient, but production releases should still be controlled and backed up.
Reproducible image releases
- Pin a WordPress image tag rather than relying on a floating
latesttag. Record the tag or digest used for every release. - For strict reproducibility, build a derived image that pins WordPress, PHP, themes and plugins. Make only the required paths writable.
- Rebuild and redeploy regularly so the image receives current security fixes, as the official image documentation recommends.
- Test updates on a staging copy with a recent database and file backup. Keep the previous image reference and a known-good backup for rollback.
Routine update sequence
- Create and verify fresh database and file backups.
- Change the image tag or derived-image version in Compose.
- Pull and recreate the services:
docker compose pull docker compose up -d - Review
docker compose psand logs, then test HTTPS, administrator access, media uploads, permalink rewrites, email delivery, cron jobs and critical plugins. - If the release fails, restore the database and files as a matched pair and redeploy the previous image version.
Custom themes, plugins and PHP extensions
Custom themes belong under /var/www/html/wp-content/themes/; plugins belong under /var/www/html/wp-content/plugins/. Mount or copy them in a way that leaves the container’s web user able to read them and, where WordPress must write, able to write them. Keep deployment files in version control and exclude secrets.
The official image does not contain every PHP extension that a plugin may require. When a plugin documents a missing extension, create a derived image with a Dockerfile based on the selected WordPress image, install the extension there, build it, and deploy that image. Installing packages manually inside a running container is not reproducible and will be lost when the container is recreated.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Apache image versus FPM
The wordpress:apache image is the simplest baseline because it includes the web server and can receive HTTP from the proxy. The wordpress:<version>-fpm image does not serve HTTP by itself; it requires a reverse proxy that speaks FastCGI.
FastCGI is inherently trusting. The official documentation warns that publishing the FPM port with Docker -p is insecure unless the port remains private. If you choose FPM, place the proxy and FPM service on a private network, publish no FPM port publicly, and configure the proxy’s FastCGI parameters correctly.
Operational checks after deployment
- DNS resolves to the VPS and the firewall exposes only the required public ports.
- The proxy reaches the WordPress service, serves the certificate and redirects HTTP to HTTPS.
- The database is reachable by service name
dband has no public port mapping. - Both named volumes exist and contain data after a container recreation.
- Logs are reviewed after startup and after every image update.
- Media uploads, permalink rewrites, email delivery, scheduled tasks and plugin compatibility are tested.
- A recent database dump and file backup exist before upgrades, and at least one restore has been rehearsed.
Common failures and fixes
The site shows a database connection error
Check docker compose logs db for initialization errors, confirm that WORDPRESS_DB_HOST is exactly db, and verify that both services use the same database name, user and secret file. A newly initialized database may need a few minutes before the first connection succeeds.
Best Value
HTTPS redirects loop or WordPress reports mixed content
Confirm the proxy sends X-Forwarded-Proto: https for TLS requests, forwards the original host, and redirects only the HTTP virtual host. Check that the canonical WordPress URL uses HTTPS and clear stale browser or plugin caches.
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 errorsUploads fail or themes cannot be updated
Inspect the WordPress volume ownership and free disk space. The container’s web user needs write access to the directories WordPress must modify, especially wp-content/uploads. Do not solve permission errors by making the entire VPS filesystem world-writable.
The container restarts after an update
Read the complete service log, verify the image tag and required PHP extensions, and compare the change with the last known-good release. Roll back the image and restore the matching backup if the database schema or plugin code is incompatible.
When a Docker VPS is the right choice
| Decision area | Self-managed Docker VPS | Managed WordPress hosting |
|---|---|---|
| Control | You choose the operating system, images, proxy, network and storage. | The provider controls much of the stack and exposes a supported configuration. |
| Reproducibility | Compose files, pinned images and derived images can reproduce a release. | Reproduction depends on the provider’s tooling and export options. |
| Security responsibility | You patch the OS, Docker, proxy, PHP, WordPress and plugins. | The provider handles the layers included in its service; you still maintain site code and credentials. |
| Recovery | You define backup scope, off-site retention, restore time and tests. | Backup frequency, retention and restore assistance depend on the plan. |
| Performance | You tune CPU, RAM, storage IOPS, caching, CDN and database settings. | Capacity and tuning are constrained by the service tier but require less administration. |
| Operations | You provide monitoring, logs, staging, rollbacks and support. | Operational tooling and support may be included, with less low-level access. |
Choose the VPS route when you need container-level control and can own patching and recovery. Choose managed hosting when reducing operational responsibility matters more than controlling the entire runtime.
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.




