This project sets up WordPress using Composer and Docker (via docker-compose for local development and docker-swarm/Portainer for production).
It places WordPress core in a subfolder and keeps wp-content
separate from the core.
- 📖 Overview
- 📂 Project Structure
- ⚙️ Environment Setup
- 🔑 Generating Secure Keys
- 🐳 Running Locally with Docker Compose
- 🌐 Deploying with Portainer (Docker Swarm)
- 🎨 Adding Themes & Plugins
- 📦 How to Move Database
- ✅ TODO List
-
Composer manages:
- WordPress core (
public/wordpress
) - Plugins (installed to
public/wp-content/plugins
) - Themes (installed to
public/wp-content/themes
) - Uploads (installs by wp to
public/wp-content/uploads
)
- WordPress core (
-
Docker containers:
compose_db
(MySQL)compose_php
(PHP-FPM with Composer and wp-cli)compose_nginx
(nginx)
-
Environment Variables via
.env
:- Database credentials (
MYSQL_ROOT_PASSWORD
,MYSQL_DATABASE
, etc.) - Debug setting
- WordPress salts (
AUTH_KEY
,SECURE_AUTH_KEY
, etc.)
- Database credentials (
.
├─ composer.json
├─ composer.lock # Generated by Composer
├─ public/
│ ├─ .env.example # Example environment file (not committed to production)
│ ├─ wp-config.php
│ ├─ index.php
│ ├─ wordpress/ # WordPress core
│ └─ wp-content/ # Custom WordPress content directory
│ ├─ themes/
│ ├─ plugins/
│ └─ uploads/
├─ deploy/
│ ├─ docker-compose.yml # Local Docker Compose
│ ├─ docker-swarm.yml # Docker Swarm / Portainer stack
│ └─ default.conf # Nginx configuration
├─ .gitignore
└─ README.md
You'll need a .env
file to store environment variables. Copy and fill (public/.env.example
) file.
Database credentials (MYSQL_ROOT_PASSWORD
, MYSQL_DATABASE
, etc.) defined in the .env
file are automatically loaded into both Docker Compose and wp-config.php
. You do not need to manually update credentials anywhere else—only the .env
file.
WordPress recommends unique salts and keys for security. You can generate them via https://roots.io/salts.html
Copy and paste the keys into your .env
under names like AUTH_KEY
, SECURE_AUTH_KEY
, LOGGED_IN_KEY
, etc.
The debug settings (DEBUG
, DEBUG_LOG
, DEBUG_DISPLAY
) defined in the .env
file directly control the debug configuration in wp-config.php
. Always manage debug configuration from your .env
file, never directly in wp-config.php
.
Security Note: Never commit your real
.env
file into source control. Use.env.example
as a template.
- Copy
.env.example
to.env
and set real values (database credentials, WP salts, etc.). - Install Composer dependencies (on your host machine or in the container):
composer install
- Start containers:
docker compose --env-file ./public/.env -f ./deploy/docker-compose.yml up -d
- Access the site at
http://localhost:NGINX_PORT
(depending on what port you mapped, e.g.8080
).
- Copy or adapt
deploy/docker-swarm.yml
for your production environment. - Provide environment variables in Portainer’s stack setup or as a
.env
file. - Deploy the stack in Portainer.
- Verify the containers start up (check logs in Portainer UI).
Since Docker Swarm containers communicate over an internal overlay network, you typically do not need to expose the MySQL port externally—only the Nginx port for incoming HTTP/HTTPS.
Many plugins and themes can be installed via WPackagist
or direct Composer packages. For example, in composer.json
:
"require": {
"wpackagist-plugin/classic-editor": "^1.6"
},
Then run:
composer update
to fetch the new plugin/theme.
It will be placed in public/wp-content/plugins/
or public/wp-content/themes/
.
If you must install a plugin that isn’t on WPackagist,
you can manually place the plugin folder in public/wp-content/plugins/
,
or a theme folder in public/wp-content/themes/
,
but that bypasses the benefits of Composer version control.
To migrate the WordPress database using wp-cli
:
wp db export db-backup.sql
This creates a SQL dump file db-backup.sql
in the current directory.
Make sure you're in the WordPress root directory, then run:
wp db import db-backup.sql
After importing, update all URLs in the database:
wp search-replace 'http://old-domain.com' 'http://new-domain.com' --skip-columns=guid
✅
--skip-columns=guid
avoids changing post GUIDs, which should stay permanent.
- Create
Dockerfile
for PHP image - Host custom image
Feel free to contribute to this project — pull requests are always welcome!
Developed in Grids by Max Ray