---
title: "How to install WordPress with Docker Compose on a VPS | StreetHosting"
description: "Run WordPress and MariaDB with Docker Compose on a VPS: named volumes, internal network, passwords in .env, HTTPS reverse proxy, backups and updates."
url: "https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps"
type: "page"
language: "en-US"
---

VPS · 9 min · Intermediate

Published on Sep 28, 2026 · Updated on Sep 28, 2026

# WordPress in containers: Compose, MariaDB and HTTPS on a VPS

With Docker Compose, WordPress and its database come up with a single command and the data lives in volumes that survive any update. See the full file, the HTTPS proxy and the backup routine.

By [Equipe StreetHosting](https://streethosting.com.br/en/autores#equipe-streethosting) · StreetHosting infrastructure and support team

[Containers and Docker](https://streethosting.com.br/en/guides/topics/docker) [Backup and recovery](https://streethosting.com.br/en/guides/topics/backup) [Databases](https://streethosting.com.br/en/guides/topics/databases) [Certificates and HTTPS](https://streethosting.com.br/en/guides/topics/ssl)

Summarize with:

[](https://chat.openai.com/?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Finstall-wordpress-docker-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "ChatGPT") [](https://claude.ai/new?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Finstall-wordpress-docker-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Claude") [](https://www.google.com/search?udm=50&aep=11&q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Finstall-wordpress-docker-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Google AI Mode") [](https://x.com/i/grok?text=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Finstall-wordpress-docker-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Grok") [](https://www.perplexity.ai/search/new?q=Summarize%20the%20key%20points%20of%20this%20StreetHosting%20guide%3A%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Finstall-wordpress-docker-vps.%20Highlight%20the%20step-by-step%20instructions%2C%20the%20prerequisites%20and%20the%20most%20common%20mistakes. "Perplexity")

Share:

[](https://x.com/intent/tweet?text=How%20to%20install%20WordPress%20with%20Docker%20Compose%20on%20a%20VPS&url=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Finstall-wordpress-docker-vps "Share on X") [](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Finstall-wordpress-docker-vps "Share on Facebook") [](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Finstall-wordpress-docker-vps "Share on LinkedIn") [](https://wa.me/?text=How%20to%20install%20WordPress%20with%20Docker%20Compose%20on%20a%20VPS%20https%3A%2F%2Fstreethosting.com.br%2Fen%2Fguides%2Fvps%2Finstall-wordpress-docker-vps "Share on WhatsApp")

For agents: Copy as Markdown [.md](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps.md)

In this guide 8 sections

* [Docker or a native install](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#docker-ou-nativo)
* [Prepare the VPS and the .env file](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#preparar)
* [The Compose file explained](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#compose)
* [Start the containers and fix errors](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#subir)
* [Reverse proxy with HTTPS](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#proxy-https)
* [Backing up the volumes](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#backup)
* [Update images without losing data](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#atualizar)
* [Which VPS to use](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#qual-vps)

Quick answer

To **install WordPress with Docker**, create a Compose file with two services, wordpress and mariadb, each with a named volume for its data, an internal network for the database and the passwords in a .env file. Publish WordPress only on 127.0.0.1 and put the host's Nginx in front of it with a Let's Encrypt certificate. The backup is a database dump plus a compressed archive of the files volume.

## Docker or a native install[](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#docker-ou-nativo)

Both approaches run the same WordPress. The difference is in how you install, isolate and update each part.

| Criterion                  | Docker Compose                                                  | Native install                               |
| -------------------------- | --------------------------------------------------------------- | -------------------------------------------- |
| Time to get it running     | Minutes, from a single file                                     | More steps, package by package               |
| Isolation between sites    | Each site with its own containers, database and PHP version     | Separate users and PHP pools, set up by hand |
| RAM usage                  | A bit higher: each site has its own Apache and its own database | Lower: services shared between the sites     |
| Switching the PHP version  | Change the image tag                                            | Install other packages on the system         |
| Rebuilding on another VPS  | Copy the folder and the volumes                                 | Redo the install and copy the data           |
| Fine-tuning the web server | Through files mounted into the container                        | Directly in the system files                 |

Docker pays off when you want pinned versions, several isolated sites or the ability to recreate an identical environment on another machine. The [native install with Nginx, PHP FPM and MariaDB](https://streethosting.com.br/en/guides/vps/host-wordpress-on-vps-nginx) squeezes more performance out of every megabyte and gives you direct access to page caching in Nginx. Neither one is wrong.

## Prepare the VPS and the .env file[](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#preparar)

You need Docker Engine with the Compose plugin. If you do not have it yet, follow [installing Docker on Ubuntu on the VPS](https://streethosting.com.br/en/guides/vps/install-docker-ubuntu-vps) and check with `docker compose version`. The domain must already point to the VPS IP as well, because HTTPS depends on it. Create the project folder and the passwords file:

`sudo mkdir -p /srv/meusite sudo chown $USER: /srv/meusite cd /srv/meusite # generate two different passwords openssl rand -base64 24 openssl rand -base64 24 nano .env chmod 600 .env`

Contents of `.env`, with the passwords you generated:

`DB_PASSWORD=cole_a_primeira_senha DB_ROOT_PASSWORD=cole_a_segunda_senha`

Also create `uploads.ini`, which raises the PHP limits inside the container. The official image uses PHP's default values, too small for uploading a video, a theme or a large plugin:

`upload_max_filesize = 64M post_max_size = 64M memory_limit = 256M`

If you keep this folder under Git, add `.env` to `.gitignore`. A database password in a repository is one of the most common leaks, and Git history keeps the file even after it is deleted.

## The Compose file explained[](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#compose)

Save it as `/srv/meusite/docker-compose.yml`:

`name: meusite services: db: image: mariadb:11.4 restart: unless-stopped environment: MARIADB_DATABASE: wordpress MARIADB_USER: wordpress MARIADB_PASSWORD: ${DB_PASSWORD} MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} volumes: - db_data:/var/lib/mysql networks: - interna healthcheck: test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] interval: 10s timeout: 5s retries: 5 start_period: 20s wordpress: image: wordpress:php8.3-apache restart: unless-stopped depends_on: db: condition: service_healthy environment: WORDPRESS_DB_HOST: db WORDPRESS_DB_NAME: wordpress WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: ${DB_PASSWORD} volumes: - wp_data:/var/www/html - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro ports: - "127.0.0.1:8080:80" networks: - interna - saida volumes: db_data: wp_data: networks: interna: internal: true saida:`

* **name:** pins the project name. The volumes become `meusite_db_data` and `meusite_wp_data`, regardless of the folder name, which simplifies backup and restore.
* **Named volumes:** the data lives in volumes managed by Docker, not inside the container. Recreating or updating the container does not touch them. The database lives in `db_data`; WordPress core, themes, plugins, uploads and configuration live in `wp_data`.
* **Internal network:** the `interna` network has no route to the outside. MariaDB exists only there and publishes no port, so nothing outside WordPress can reach the database. WordPress also joins the `saida` network to download plugins, themes and updates.
* **Variables:** Compose reads the `.env` from the same folder and substitutes the password references. The Compose file can go into Git without exposing anything.
* **Healthcheck:** WordPress only starts after MariaDB accepts connections, which avoids the database connection error on the first boot.
* **Port on 127.0.0.1:** Docker writes its own rules into the kernel firewall, and a port published on all interfaces bypasses UFW. Bound to loopback, it can only be reached by the proxy on the VPS itself.

A common variation is to swap the `wp_data` volume for a host folder, such as `./wp:/var/www/html`. It works and makes editing a theme over SFTP easier, but the files then belong to the container's numeric user, and any permission change made on the host affects the site. If you do not plan to touch the files from outside, the named volume is less work and is what the backup commands in this guide expect.

The `php8.3-apache` tag tracks the latest WordPress release with PHP 8.3. To pin the major version, use the `7-php8.3-apache` format. Check the available tags on Docker Hub before switching, because the PHP versions on offer change over time.

## Start the containers and fix errors[](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#subir)

`cd /srv/meusite docker compose up -d docker compose ps docker compose logs -f wordpress curl -I http://127.0.0.1:8080`

Running `docker compose ps` should show the database as healthy and WordPress running. The curl should return a redirect to the installation page. On the first start, the image copies WordPress into the volume and generates the configuration file with random salts, which are stored in the volume itself.

Do not open the installer yet. Set up the HTTPS proxy first, so WordPress records the site address with https from the start and you do not have to fix URLs later.

If something does not come up, these are the errors that show up most often in this setup and what each one usually means:

| Symptom                                                | Likely cause                                                                                                                | Fix                                                                                                      |
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Error establishing a database connection               | The password in .env was changed after the first start. MariaDB only reads those variables when it creates the empty volume | Put the original password back in .env or change the user's password inside the database with ALTER USER |
| Redirect loop or unstyled page                         | The proxy does not tell WordPress that the original connection is HTTPS                                                     | Send the protocol header in the server block, as shown below, and reload Nginx                           |
| 413 Request Entity Too Large on upload                 | The host Nginx body size limit is smaller than the file                                                                     | Adjust client\_max\_body\_size along with uploads.ini                                                    |
| WordPress asks for FTP credentials to install a plugin | Files in the volume have the wrong owner, common after restoring a backup                                                   | Give ownership of the files back to the Apache user in the container                                     |
| port is already allocated on start                     | Another application is already using port 8080 on loopback                                                                  | Switch to 8081 in the Compose file and in proxy\_pass                                                    |

For the permissions case, the Apache user in the official image is `www-data`, and a single command fixes it: `docker compose exec wordpress chown -R www-data:www-data /var/www/html`. To see what the database is saying, use `docker compose logs db`; the first initialization logs the creation of the user and the database there.

## Reverse proxy with HTTPS[](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#proxy-https)

Nginx runs on the host, takes ports 80 and 443 and forwards traffic to the container. Install it with `sudo apt install -y nginx certbot python3-certbot-nginx` and create `/etc/nginx/sites-available/seu-dominio.com.br`:

`server { listen 80; server_name seu-dominio.com.br www.seu-dominio.com.br; 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; } }`

`sudo ln -s /etc/nginx/sites-available/seu-dominio.com.br /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx sudo ufw allow 'Nginx Full' sudo certbot --nginx -d seu-dominio.com.br -d www.seu-dominio.com.br`

The `X-Forwarded-Proto` header is the detail that breaks the most installs. The configuration file the official image generates marks the request as HTTPS when that header says https. Without it, WordPress thinks it is on HTTP and falls into a redirect loop or loads CSS over HTTP, which the browser blocks. The proxy fundamentals are in [Nginx as a reverse proxy on the VPS](https://streethosting.com.br/en/guides/vps/nginx-reverse-proxy-vps). If you prefer a web interface to manage domains and certificates, [Nginx Proxy Manager](https://streethosting.com.br/en/guides/vps/install-nginx-proxy-manager-on-vps) does the same job.

Once the certificate is issued, open `https://seu-dominio.com.br` and finish the installer. Security plugins that log the visitor's IP must be configured to read `X-Forwarded-For`, because to the container's Apache every connection comes from the proxy.

## Backing up the volumes[](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#backup)

There are two copies: the database, via a dump, and the files volume, via a compressed archive. Put both in one script, to run by hand today and from cron later. Create `/usr/local/bin/backup-meusite.sh`:

`#!/bin/sh set -e DESTINO=/srv/backups DATA=$(date +%F) mkdir -p "$DESTINO" cd /srv/meusite # database: consistent dump without stopping the site docker compose exec -T db sh -c 'exec mariadb-dump --single-transaction -uroot -p"$MARIADB_ROOT_PASSWORD" wordpress' > "$DESTINO/wp-banco-$DATA.sql" # files: core, themes, plugins, uploads and configuration docker run --rm -v meusite_wp_data:/dados:ro -v "$DESTINO":/backup alpine tar -czf "/backup/wp-arquivos-$DATA.tar.gz" -C /dados . # keep seven days of local copies find "$DESTINO" -name 'wp-*' -mtime +7 -delete`

`sudo chmod 700 /usr/local/bin/backup-meusite.sh sudo /usr/local/bin/backup-meusite.sh ls -lh /srv/backups # schedule it every day at 3:30 AM: open root's crontab sudo crontab -e # and add this line in the editor 30 3 * * * /usr/local/bin/backup-meusite.sh`

Run it once by hand and check that both files have a plausible size before trusting the schedule. The dump with `--single-transaction` produces a consistent copy of the database with the site live. Copying the files out of the MariaDB volume while the service is running can produce a backup that will not open. To restore, the path is the reverse:

`cd /srv/meusite docker compose exec -T db sh -c 'exec mariadb -uroot -p"$MARIADB_ROOT_PASSWORD" wordpress' < /srv/backups/wp-banco-AAAA-MM-DD.sql docker run --rm -v meusite_wp_data:/dados -v /srv/backups:/backup alpine sh -c "cd /dados && tar -xzf /backup/wp-arquivos-AAAA-MM-DD.tar.gz"`

The most important part is still missing: getting the copies off the VPS. A backup that lives on the same disk disappears along with it, and on a VPS the backup routine is the responsibility of whoever administers the server. The guide on [automated VPS backups with restic and cron](https://streethosting.com.br/en/guides/vps/vps-backup-restic-cron) shows how to send the backups folder, encrypted, to another server or S3-compatible storage, with retention and restore testing.

## Update images without losing data[](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#atualizar)

`cd /srv/meusite docker compose pull docker compose up -d docker image prune -f`

Compose recreates only the containers whose image changed, and the volumes stay where they were. There is a detail that confuses a lot of people: the WordPress image copies the core into the volume only when it is empty. A new image updates PHP and Apache, but the WordPress inside the volume stays on the same version. Core, plugins and themes update through the dashboard, as in any install.

* **MariaDB within the same series:** the `11.4` tag gets fixes within the series with a simple pull.
* **MariaDB to another series:** take the dump first, change the tag and add `MARIADB_AUTO_UPGRADE: "1"` to the service environment, so the image upgrades the system tables on startup.
* **New PHP version:** test the new tag on a copy of the site before switching in production. Old plugins are what usually breaks.

If you would rather follow containers, logs and volumes through an interface, you can install [Portainer to manage Docker](https://streethosting.com.br/en/guides/vps/install-portainer-docker-vps), with access protected by HTTPS and a strong password.

## Which VPS to use[](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#qual-vps)

Each site on Docker adds one WordPress container with Apache and one MariaDB container. A site with modest traffic usually stays under 1 GB for the two combined; check real usage with `docker stats` after a few days live. With several sites, multiply: five projects mean five databases and five web servers taking up memory, even if four of them get few visits. Leave disk space as well for the images, the volumes and the local backup copies, which grow along with the uploads folder.

| Scenario                                       | Starting plan                           | Monthly price |
| ---------------------------------------------- | --------------------------------------- | ------------- |
| One light site or a test environment           | Xeon VPS 2 vCPU, 2 GB, 20 GB NVMe       | R$ 26.00      |
| One to three sites with caching                | Xeon VPS 3 vCPU, 4 GB, 40 GB NVMe       | R$ 43.00      |
| A store or a site with lots of dynamic content | Ryzen VPS 2 vCPU, 4 GB DDR5, 40 GB NVMe | R$ 66.00      |
| Several sites, each with its own stack         | Xeon VPS 6 vCPU, 8 GB, 80 GB NVMe       | R$ 77.00      |

Both lines run in São Paulo with NVMe, root access, Anti-DDoS included and activation within 60 seconds. The Xeon delivers more vCPU for the money when you run several sites; the Ryzen 9 9950X, with clocks up to 5.7 GHz, responds faster on pages that are not served from cache. See every tier on the [StreetHosting VPS page](https://streethosting.com.br/en/vps).

In this guide

* [Docker or a native install](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#docker-ou-nativo)
* [Prepare the VPS and the .env file](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#preparar)
* [The Compose file explained](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#compose)
* [Start the containers and fix errors](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#subir)
* [Reverse proxy with HTTPS](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#proxy-https)
* [Backing up the volumes](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#backup)
* [Update images without losing data](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#atualizar)
* [Which VPS to use](https://streethosting.com.br/en/guides/vps/install-wordpress-docker-vps#qual-vps)

## Frequently asked questions

Does Docker make WordPress slower?

Not noticeably. Containers run directly on the VPS kernel, with no extra virtualization layer. The difference comes from the official image using Apache with embedded PHP, which uses more memory per connection than Nginx with PHP FPM; with page caching, the visitor rarely notices.

Do I lose my data if I delete the container?

No, as long as it is in volumes. Stopping, removing or recreating containers leaves the volumes intact. They are only deleted with docker compose down -v or docker volume rm, so never use those options without a recent backup.

How do I update WordPress running in Docker?

Core, plugins and themes are updated through the WordPress dashboard, because they live in the volume. Updating the image with docker compose pull refreshes PHP and Apache, but does not replace the WordPress files already in the volume.

Can I run several WordPress sites with Docker on the same VPS?

Yes. Create one folder per site, each with its own Compose file, its own project name and a different port on 127.0.0.1, and point a server block in the host Nginx at each port. Each site gets its own database and PHP version.

Why publish the WordPress port only on 127.0.0.1?

Because Docker creates its own rules in the kernel firewall, and a port published on all interfaces is reachable from the internet even with UFW blocking it. On 127.0.0.1, only the Nginx on the VPS itself reaches the container, and all external access goes through HTTPS.

Next step

See VPS plans

Root VPS in Brazil with NVMe and Anti-DDoS.

[See VPS plans](https://streethosting.com.br/en/vps)

[See Xeon VPS Xeon VPS for steady workloads, automation and long-running projects.](https://streethosting.com.br/en/vps/xeon) [See Ryzen VPS Ryzen 9 9950X VPS in São Paulo with root access, NVMe and gamer Anti-DDoS.](https://streethosting.com.br/en/vps/ryzen)

## Related guides

[VPS Intermediate How to install Docker on Ubuntu 22.04 or 24.04 on a VPS (straight to the point) A stable Docker install on an Ubuntu VPS depends on the right package source and step by step validation. In this guide you set up the official repository, install Engine and Compose, test the daemon and apply basic security measures before deploying applications. 3 min Read guide](https://streethosting.com.br/en/guides/vps/install-docker-ubuntu-vps) [VPS Intermediate Host WordPress on a VPS: Nginx, PHP 8.3 and MariaDB Installing WordPress by hand, with no control panel, gives you full control over performance and security. The path is short: Nginx, PHP FPM and MariaDB on Ubuntu 24.04, a dedicated database, HTTPS and two layers of cache. 9 min Read guide](https://streethosting.com.br/en/guides/vps/host-wordpress-on-vps-nginx) [VPS Intermediate How to set up Nginx as a reverse proxy on a VPS Your app runs on an internal port and you want to serve it on a domain with HTTPS. Nginx as a reverse proxy solves that and also brings several apps together in one place. 3 min Read guide](https://streethosting.com.br/en/guides/vps/nginx-reverse-proxy-vps)

[← Back to the Guide Center](https://streethosting.com.br/en/guides)
