An open API service indexing awesome lists of open source software.

https://github.com/nooblk-98/wordpress-docker-fpm-nginx

Production-ready WordPress stack with PHP-FPM, Nginx, MariaDB and Certbot SSL fully containerized with Docker Compose
https://github.com/nooblk-98/wordpress-docker-fpm-nginx

docker docker-compose letsencrypt mariadb nginx php-fpm ssl wordpress wordpress-docker

Last synced: 9 days ago
JSON representation

Production-ready WordPress stack with PHP-FPM, Nginx, MariaDB and Certbot SSL fully containerized with Docker Compose

Awesome Lists containing this project

README

          

# WordPress FPM + Nginx

[![GitHub Repo](https://img.shields.io/badge/GitHub-wordpress--docker--fpm--nginx-181717?style=flat-square&logo=github)](https://github.com/nooblk-98/wordpress-docker-fpm-nginx)
[![License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)](LICENSE)

**Multi-site WordPress hosting on a single VPS.** Nginx on the host, per-site PHP-FPM in Docker, shared MariaDB/MySQL and Redis.

[Features](#features) • [Architecture](#architecture) • [Prerequisites](#prerequisites) • [Quick start](#quick-start) • [Management menu](#management-menu) • [CLI reference](#cli-reference) • [Backups](#backups) • [Troubleshooting](#troubleshooting)

---

## Features

- **Multi-site** — each site gets its own PHP container and Nginx config on a single VPS
- **Per-site PHP versions** — choose PHP 7.4, 8.0, 8.1, 8.2, 8.3, or 8.4 per site
- **Database choice** — MariaDB or MySQL, picked during setup
- **Automatic SSL** — Let's Encrypt via Certbot with auto-renewal
- **One-command bootstrap** — go from a blank VPS to a running WordPress server with a single curl command
- **SQLite state database** — all site metadata tracked locally (domains, credentials, PHP versions, ports)
- **Automated backups** — per-site file and database backups with point-in-time restore from the menu
- **Management menu** — 20-option interactive menu covering site management, WP updates, password reset, resource monitoring, and more

## Architecture

Nginx runs on the host (not in Docker), handling SSL termination and proxying to per-site PHP-FPM containers. MariaDB/MySQL and Redis run as shared Docker containers.

```
Host (VPS)
├── Nginx :80/:443 (host process)
│ └── proxy_pass to PHP containers
└── Docker network
├── wp_php_site-a :9001 (PHP-FPM, per site)
├── wp_php_site-b :9002 (PHP-FPM, per site)
├── wp_db :3306 (MariaDB/MySQL, shared)
└── wp_redis :6379 (Redis, shared)
```

Each site gets its own PHP container with a unique port so PHP versions are isolated. If one site crashes its PHP process the others keep running.

All sites share a single MariaDB/MySQL instance with separate databases and users, and a single Redis instance for object caching.

## Prerequisites

- A Linux VPS (Ubuntu 22.04+ or Debian 12+ recommended)
- Root access
- A domain name pointed at your VPS IP (required for SSL)
- Ports 80 and 443 open in your firewall

## Quick start

### One-command install

Run this on a fresh VPS as root:

```bash
bash <(curl -fsSL https://raw.githubusercontent.com/nooblk-98/wordpress-docker-fpm-nginx/main/install.sh)
```

This will:

1. Install git and clone the repository to `/opt/wp-manager`
2. Run the core infrastructure setup
3. Enable the management menu on SSH login
4. Launch the interactive menu

### Step-by-step

```bash
git clone https://github.com/nooblk-98/wordpress-docker-fpm-nginx.git
cd wordpress-docker-fpm-nginx
sudo bash src/install.sh
```

During setup you will be prompted for:

- Database engine (MariaDB or MySQL) and version
- Database passwords (auto-generated if left blank)

> [!NOTE]
> The setup installs Nginx, Certbot, Docker, and starts MariaDB/MySQL + Redis containers. WordPress is **not** installed during infrastructure setup. Sites are added through the management menu.

## Management menu

```bash
sudo bash src/menu.sh
```

The menu shows system resources and service status, then lists options by category:

```
--- Sites ---
1) Add New WordPress Site
2) List All Sites
3) Enable / Disable a Site
4) Remove a Site
--- WordPress ---
5) Update WordPress Core & Plugins
6) Reset WordPress Admin Password
7) Change PHP Version for a Site
--- Backup & Restore ---
8) Backup a Site
9) Restore a Site
10) Manage Backups (List / Delete)
--- System ---
11) Service Status Overview
12) Container Resource Usage
13) Disk Usage Per Site
14) Restart Services
15) View Logs
16) Renew All SSL Certificates
17) SSL Certificate Details
--- Settings ---
18) Update WP-Manager
19) Uninstall PHP Container
20) Toggle auto-show on SSH login
0) Exit
```

### Adding a site

Select option **1** and provide:

| Prompt | Example |
|---|---|
| Site label | `myshop` (used for DB/user/container names) |
| Domain | `example.com` |
| SSL | yes/no, optionally include `www.example.com` |
| PHP version | `8.3` (default) |

The script will:

1. Create a system user for the site
2. Build a PHP-FPM Docker image (if not cached) and start a container on an available port
3. Create an Nginx server block with security headers
4. Create a dedicated MariaDB/MySQL database and user
5. Download WordPress via WP-CLI and create `wp-config.php`
6. Optionally obtain a Let's Encrypt SSL certificate
7. Record everything in the SQLite state database

> [!TIP]
> The PHP image build takes 2-3 minutes the first time (compiling imagick and redis extensions). Subsequent site additions reuse the cached image and take around 10 seconds.

### Enabling / Disabling a site

Select option **3** to take a site offline without deleting it. The Nginx config is renamed to `.conf.disabled` and Nginx reloads. All files, the database, and the PHP container remain untouched. Re-enabling reverses this instantly.

### Updating WordPress

Select option **5** to run `core update`, `plugin update --all`, and `theme update --all` via WP-CLI inside the site's PHP container. Works on a single site or all sites at once.

### Resetting the admin password

Select option **6** to list admin accounts for a site and set a new password via WP-CLI.

### Changing PHP version

Select option **7** to stop a site's PHP container, pull the new PHP version image, and restart.

### Backup and restore

- **Option 8** — create a timestamped backup (files + database) for a site
- **Option 9** — restore a site from any existing backup; file and database restores are selected independently
- **Option 10** — list all backups with sizes, delete a single file, or wipe all backups for a domain

### Container resource usage

Select option **12** for a `docker stats` snapshot showing CPU, memory, network I/O, and disk I/O for all PHP containers.

### Disk usage per site

Select option **13** for a table of disk usage per site under `/var/www`, with the total at the bottom.

### Updating WP-Manager

Select option **18** to check for upstream updates and pull the latest version. The current and remote commit hashes are shown before you confirm.

### Uninstalling a PHP container

Select option **19** to remove a stopped or unused PHP-FPM container and optionally its image. Site files and the database are not affected.

## CLI reference

All operations are available as standalone scripts:

```bash
# Infrastructure setup
sudo bash src/install.sh

# Add a site
sudo bash src/add-site.sh example.com \
--php-version 8.3 \
--email admin@example.com \
--ssl

# Remove a site (auto-backup before removal)
sudo bash src/remove-site.sh example.com
sudo bash src/remove-site.sh example.com --force # skip confirmation

# Backup a site
sudo bash src/backup.sh example.com

# System status
sudo bash src/status.sh # human-readable
sudo bash src/status.sh --json # JSON output
```

## Managing multiple sites

Each site gets isolated resources:

| Resource | Detail |
|---|---|
| **PHP container** | `wp_php_` |
| **PHP port** | unique per site, starting at 9000 |
| **Nginx config** | `/etc/nginx/conf.d/.conf` |
| **Database** | `_db` with dedicated user |
| **System user** | dedicated Unix user |
| **Site files** | `/var/www//` |

Shared resources:

| Resource | Detail |
|---|---|
| **MariaDB/MySQL** | `wp_db` container |
| **Redis** | `wp_redis` container |
| **Docker network** | `wp_network` |
| **Certbot** | single install with auto-renewal timer |
| **State database** | `state.db` (SQLite) |

## Backups

Backups can be created from the menu (option **8**) or via CLI:

```bash
sudo bash src/backup.sh example.com

# Custom output directory
sudo bash src/backup.sh example.com --output /mnt/backups
```

Output files:

```
backups/
example.com_files_2026-05-25_093000.tar.gz
example.com_db_2026-05-25_093000.sql
```

A backup is also created automatically when a site is removed.

### Restoring a site

Use menu option **9** to restore from any existing backup. File and database restores are independent, so you can roll back just the database after a bad plugin update without touching the files.

Use menu option **10** to list backup sizes, delete individual files, or remove all backups for a domain.

## State database

All site metadata is stored in `state.db` (SQLite):

```bash
python3 src/lib/state.py list # list all sites
python3 src/lib/state.py list --json # JSON output
python3 src/lib/state.py get example.com # single site as JSON
python3 src/lib/state.py count # site count
python3 src/lib/state.py config-list # global config
```

Stored fields: domain, site directory, PHP version, port, database name/user/password, system user, SSL status, WordPress install status, and timestamps.

## File structure

```
wordpress-docker-fpm-nginx/
├── install.sh # one-command VPS bootstrapper
├── docker-compose.yml # shared services (DB, Redis)
├── nginx/
│ ├── conf.d/
│ │ └── wordpress.conf # default Nginx server block
│ ├── includes/
│ │ └── wordpress-base.conf # shared Nginx includes (security headers, locations)
│ └── nginx.conf # global Nginx config
├── php/
│ ├── Dockerfile # PHP image with imagick, redis, WP-CLI
│ └── php.ini # PHP config overrides
└── src/
├── install.sh # core infrastructure installer
├── menu.sh # interactive management menu
├── add-site.sh # provision a new WordPress site
├── remove-site.sh # remove a site with rollback
├── backup.sh # file + database backup
├── status.sh # system health check (text or JSON)
└── lib/
├── common.sh # shared helpers (colors, logging)
├── docker.sh # Docker container management
├── nginx.sh # Nginx config generation
├── state.py # SQLite state database (Python CLI)
└── state.sh # Bash wrappers for state.py
```

## Rollback safety

Both `add-site.sh` and `remove-site.sh` register undo operations before each destructive step. If any step fails, the rollback runs in reverse order and cleans up any created resources (users, containers, databases, configs).

## Troubleshooting

### PHP container fails to start

```bash
docker logs wp_php_

docker rm -f wp_php_
sudo bash src/add-site.sh --php-version
```

### Nginx configuration error

```bash
nginx -t
nginx -s reload
```

### SSL certificate renewal fails

```bash
certbot renew --dry-run
certbot renew
```

### Docker network issues

```bash
docker network ls
docker network inspect wordpress-docker-fpm-nginx_wp_network
```

### WordPress admin password

Use menu option **6** to reset the admin password without needing to log in to WordPress.

### Database credentials

All credentials are stored in the state database:

```bash
python3 src/lib/state.py get
```

> [!WARNING]
> The state database contains plaintext database passwords. Keep `state.db` secure and never commit it to version control.