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
- Host: GitHub
- URL: https://github.com/nooblk-98/wordpress-docker-fpm-nginx
- Owner: nooblk-98
- Created: 2026-04-24T10:53:56.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-05-26T06:28:14.000Z (2 months ago)
- Last Synced: 2026-07-17T15:38:49.182Z (9 days ago)
- Topics: docker, docker-compose, letsencrypt, mariadb, nginx, php-fpm, ssl, wordpress, wordpress-docker
- Language: Shell
- Homepage:
- Size: 342 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# WordPress FPM + Nginx
[](https://github.com/nooblk-98/wordpress-docker-fpm-nginx)
[](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.