How to install WordPress on a VPS with Docker

WordPress powers a huge slice of the web, and for good reason. It’s flexible, it’s well documented, and there’s a plugin for almost anything you can dream up. Running it on your own VPS with Docker gives you something the big shared hosts can’t: full control over your stack, clean isolation between services, and a setup you can tear down and rebuild in minutes. This guide walks you through the whole process, from a fresh server to a live HTTPS site, using Docker Compose to keep everything tidy.

By the end you’ll have WordPress, a MariaDB database, and an Nginx reverse proxy all working together, with a free SSL certificate from Let’s Encrypt sitting on top. Let’s get into it.

What Is WordPress and Why Run It in Docker?

WordPress is an open-source content management system. It started life as blogging software back in 2003 and grew into a full platform that runs everything from personal sites to large online stores. You write content through a friendly admin dashboard, pick a theme to control how things look, and bolt on plugins to add features. No coding required for the basics.

So why bother with Docker instead of just dropping the files on a server the old-fashioned way? A few reasons stand out. Docker packages WordPress and its dependencies into containers, which means the exact same setup runs the same way on your laptop, your VPS, or a colleague’s machine. There’s no “works on my server” headache. You also get clean separation. Your database lives in one container and your web application in another, so updating one won’t accidentally break the other. And when something does go wrong, you can rebuild the whole stack from a single configuration file rather than retracing a dozen manual steps you half remember.

This guide uses Docker Compose, a tool that defines your entire multi-container setup in one readable file. Spin everything up with one command. Shut it all down with another. That’s the appeal.

Prerequisites

Before you start, make sure you’ve got the essentials lined up. Here’s what you’ll need on the server side.

Requirement Minimum Recommended
Operating System Ubuntu 22.04 LTS Ubuntu 24.04 LTS
RAM 1 GB 2 GB or more
CPU 1 vCPU 2 vCPU
Storage 20 GB SSD 40 GB SSD
Software Docker Engine, Docker Compose Docker Engine, Docker Compose

You’ll also want a few things sorted out ahead of time:

  • A registered domain name pointed at your server’s public IP address using an A record. SSL won’t work without this.
  • Root access or a user account with sudo privileges.
  • A non-root user is best practice for day-to-day work. If you’re logged in as root, consider creating one first.

Got all that? Good. Time to install Docker.

Step-by-Step Installation

Step 1: Update Your System

Always start with a fresh package list. Log into your server over SSH and run the following:

sudo apt update && sudo apt upgrade -y

This pulls down the latest package information and upgrades anything that’s out of date. It might take a minute or two depending on how stale your image is.

Step 2: Install Docker Engine

Next, install Docker from its official repository rather than the version in Ubuntu’s default repos, which tends to lag behind. First, set up the prerequisites and add Docker’s GPG key:

sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

Now add the Docker repository to your sources:

echo /
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu /
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | /
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Update your package list again so it picks up the new repository, then install Docker Engine along with the Compose plugin:

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Confirm everything installed correctly:

sudo docker --version
sudo docker compose version

You should see version numbers print for both. If you’d rather not type sudo in front of every Docker command, add your user to the docker group:

sudo usermod -aG docker $USER

Log out and back in for that change to take effect.

Step 3: Create Your Project Directory

Keep your configuration organized in its own folder. Create one and move into it:

mkdir -p ~/wordpress-docker
cd ~/wordpress-docker

Everything from here on lives inside this directory.

Step 4: Set Up Your Environment Variables

Rather than hardcoding passwords into your Compose file, store them in a separate .env file. This keeps secrets out of your main config and makes them easy to change. Create the file:

nano .env

Paste in the following, swapping the placeholder values for strong, unique passwords of your own:

MYSQL_ROOT_PASSWORD=change_this_root_password
MYSQL_DATABASE=wordpress
MYSQL_USER=wp_user
MYSQL_PASSWORD=change_this_user_password

Save and exit. In nano that’s Ctrl+O, Enter, then Ctrl+X. Never commit this file to a public repository. If you use Git, add it to your .gitignore right away.

Step 5: Write the Docker Compose File

This is the heart of the whole setup. The Compose file describes every container, how they connect, and where their data lives. Create it:

nano docker-compose.yml

Paste in the configuration below:

services:
  db:
    image: mariadb:11.4
    container_name: wordpress_db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - wordpress_net
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
      timeout: 5s
      retries: 5

  wordpress:
    image: wordpress:6.5-php8.3-apache
    container_name: wordpress_app
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
      WORDPRESS_DB_USER: ${MYSQL_USER}
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - wordpress_data:/var/www/html
    ports:
      - "127.0.0.1:8080:80"
    networks:
      - wordpress_net

volumes:
  db_data:
  wordpress_data:

networks:
  wordpress_net:

A few things in here are worth understanding. The depends_on block with a service_healthy condition tells WordPress to wait until the database is genuinely ready before starting, which sidesteps a common race condition where WordPress boots first and can’t find its database. The healthcheck on the db service is what makes that condition work.

Notice the ports line too. We bind WordPress to 127.0.0.1:8080 rather than 0.0.0.0:8080. That means the container only listens on localhost and isn’t exposed directly to the internet. Nginx will handle the public-facing traffic and forward it inward, which is both cleaner and safer. The named volumes, db_data and wordpress_data, keep your database and site files on disk so nothing vanishes when a container restarts.

Save and exit when you’re done.

Step 6: Launch the Containers

With your Compose file ready, bring the stack to life:

docker compose up -d

The -d flag runs everything in detached mode, meaning it’ll keep running in the background after you close your terminal. Docker downloads the images on first run, so give it a moment. Check that both containers are up:

docker compose ps

You should see wordpress_db and wordpress_app both listed as running. You can confirm WordPress is responding locally with:

curl -I http://127.0.0.1:8080

A 200 or 302 response means WordPress is alive and well. Now let’s make it reachable from the outside world.

Step 7: Install and Configure Nginx

Nginx will sit in front of WordPress as a reverse proxy. Install it directly on the host:

sudo apt install -y nginx

Create a server block for your site. Replace example.com with your actual domain throughout:

sudo nano /etc/nginx/sites-available/wordpress

Add this configuration:

server {
    listen 80;
    server_name example.com www.example.com;

    client_max_body_size 64M;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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;
    }
}

Those proxy_set_header lines matter. They pass the original visitor details through to WordPress so it knows the real hostname and protocol, which prevents redirect loops and broken links down the line. The client_max_body_size directive lets you upload larger media files and themes without hitting Nginx’s default limit.

Enable the site by linking it into the sites-enabled directory, then remove the default config so it doesn’t get in the way:

sudo ln -s /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default

Test the configuration for syntax errors and reload Nginx:

sudo nginx -t
sudo systemctl reload nginx

Visit your domain in a browser now and you should see the WordPress installation screen over plain HTTP. Don’t run through the setup just yet. Let’s lock down HTTPS first.

Step 8: Secure Your Site with Let’s Encrypt SSL

A free SSL certificate from Let’s Encrypt encrypts traffic between your visitors and your server. Certbot automates the whole thing. Install it along with its Nginx plugin:

sudo apt install -y certbot python3-certbot-nginx

Request and install your certificate in one go:

sudo certbot --nginx -d example.com -d www.example.com

Certbot will ask for an email address and prompt you to agree to the terms of service. When it asks whether to redirect HTTP traffic to HTTPS, choose the redirect option. This forces every visitor onto the secure version of your site automatically. Certbot edits your Nginx config, fetches the certificate, and reloads everything for you.

Certificates from Let’s Encrypt last 90 days, but Certbot sets up automatic renewal during installation. You can confirm the renewal timer is active and do a dry run to make sure it’ll work when the time comes:

sudo certbot renew --dry-run

If that completes without errors, you’re set. Renewals will happen quietly in the background.

Step 9: Complete the WordPress Setup

Now for the fun part. Open your domain in a browser using https://. You’ll land on the famous five-minute WordPress install. Pick your language, then fill in your site details:

  • A site title.
  • An admin username. Avoid “admin” since it’s the first thing attackers guess.
  • A strong password. WordPress generates a good one for you.
  • Your email address for password recovery and notifications.

Click install, log in, and you’re looking at your brand new WordPress dashboard. Congratulations, your site is live, containerized, and secured with HTTPS.

How to Update WordPress and Your Containers

Keeping things current is mostly painless with Docker. There are two layers to think about: the WordPress software itself and the underlying container images.

WordPress core, themes, and plugins update straight from the admin dashboard, just like any normal install. Head to the Updates section and click the buttons. Because your files live in a persistent volume, these updates stick around between container restarts.

Updating the container images themselves, say to move to a newer PHP version or a patched MariaDB release, takes a few commands. First, edit your docker-compose.yml and change the image tags to the versions you want. Then pull the new images and recreate the containers:

cd ~/wordpress-docker
docker compose pull
docker compose up -d

Docker only recreates containers whose images actually changed, and your volumes stay untouched, so your content and database survive the upgrade. Before any major version jump, though, back up first. Always.

To clean up old, unused images and reclaim some disk space afterward, run:

docker image prune -f

Troubleshooting Common Issues

Even a clean setup throws the occasional curveball. Here are five problems you might run into and how to fix them.

Error Establishing a Database Connection

This usually means WordPress can’t reach MariaDB. Check that both containers are running with docker compose ps. If the database container keeps restarting, view its logs:

docker compose logs db

Nine times out of ten it’s a mismatch between the credentials in your .env file and what WordPress is trying to use. Make sure the database name, user, and password line up across both files. If you changed the .env after the first launch, the database was already created with the old values, so you may need to recreate the volume.

502 Bad Gateway from Nginx

A 502 means Nginx is up but can’t talk to WordPress behind it. Confirm the WordPress container is actually listening on port 8080:

curl -I http://127.0.0.1:8080

No response? The container might still be starting or may have crashed. Check its logs with docker compose logs wordpress. Also double-check that the proxy_pass address in your Nginx config matches the port you bound in the Compose file.

Stuck in an HTTPS Redirect Loop

If your site endlessly redirects after enabling SSL, WordPress probably isn’t aware it’s being served over HTTPS. The X-Forwarded-Proto header in your Nginx config handles most of this, but you may need to nudge WordPress. Add these lines near the top of your wp-config.php file, just before the line that says “That’s all, stop editing”:

if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

You can reach this file by entering the container with docker compose exec wordpress bash and editing it there, or by mapping it out as a volume.

Maximum Upload Size Too Small

Trying to upload a theme or large image and hitting a size wall? Two limits are at play. You already raised the Nginx limit with client_max_body_size. The other lives in PHP. Create a custom config file and mount it into your WordPress container. Add a small uploads.ini to your project folder:

file_uploads = On
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300

Then add a volume line under the wordpress service in your Compose file pointing to /usr/local/etc/php/conf.d/uploads.ini, and recreate the container. Your new limits will take hold.

Containers Won’t Start After a Reboot

If your site goes dark after the server restarts, check that your containers have the right restart policy. The Compose file in this guide uses restart: unless-stopped, which brings containers back automatically after a reboot. Verify Docker itself starts on boot:

sudo systemctl enable docker

With that enabled and the restart policy in place, your stack will recover on its own the next time the server cycles.

What to Do Next

Your WordPress site is running, but a few extra steps will make it sturdier and more pleasant to manage over time.

  • Set up automated backups. Schedule a cron job to dump your database and archive your wordpress_data volume on a regular basis. Off-site storage is even better.
  • Install a security plugin. Tools like Wordfence add a firewall, login protection, and malware scanning on top of your server-level setup.
  • Add a caching layer. A plugin such as WP Super Cache or Redis object caching will noticeably speed up page loads, especially on a smaller VPS.
  • Harden your server. Configure a firewall with UFW to allow only the ports you need, and consider disabling password-based SSH in favor of key authentication.
  • Monitor your resources. Keep an eye on memory and disk usage so you know when it’s time to scale up.

Each of these is worth its own deep dive, and you can layer them in as your site grows.

Wrapping Up

You’ve gone from a bare VPS to a fully functional WordPress site running in Docker, complete with a reverse proxy and a free SSL certificate. More importantly, you’ve built it in a way that’s reproducible and easy to maintain. Your whole stack lives in a couple of config files, your data sits safely in volumes, and updates are a matter of a command or two. That’s the kind of setup that saves you headaches months from now when you’ve half forgotten how any of it works. Go build something great on it.

The post How to install WordPress on a VPS with Docker appeared first on GreenGeeks.

版权声明:
作者:lichengxin
链接:https://www.techfm.club/p/236882.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>