October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
VGSources
Blog

How to Run WordPress in Docker on a VPS (2026 Guide)

A production-minded 2026 guide to running WordPress and MySQL in Docker Compose on a VPS, with persistent storage, HTTPS, secrets, backups, updates and recovery steps.
WorldDocker on a VPS Length10 min Posted Quest giverVGSources Team

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What the deployment looks like

The public request path should be:

  1. A DNS A or AAAA record points your domain to the VPS.
  2. A reverse proxy such as NGINX or Apache accepts ports 80 and 443, obtains and renews the TLS certificate, and redirects HTTP to HTTPS.
  3. The proxy forwards application traffic to the WordPress container on a private Docker network.
  4. 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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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

  1. Install Docker Engine and the Compose plugin using your distribution’s supported instructions.
  2. Create the project directory, secret file and Compose file shown above.
  3. Point the domain’s A or AAAA record at the VPS. DNS changes may take time to propagate.
  4. From the project directory, download the selected images:
    docker compose pull
  5. Start the services in the background:
    docker compose up -d
  6. Check container state and recent logs:
    docker compose ps
    docker compose logs --tail=100 wordpress db
  7. 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Restore procedure

  1. Stop writes or place the site in maintenance mode so the restored files and database represent the same point in time.
  2. Restore the file archive into the WordPress volume, preserving ownership and permissions expected by the image.
  3. 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"'
  4. 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 latest tag. 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

  1. Create and verify fresh database and file backups.
  2. Change the image tag or derived-image version in Compose.
  3. Pull and recreate the services:
    docker compose pull
    docker compose up -d
  4. Review docker compose ps and logs, then test HTTPS, administrator access, media uploads, permalink rewrites, email delivery, cron jobs and critical plugins.
  5. 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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 db and 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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Uploads 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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.