This repository hosts a Docker Compose-based infrastructure inspired by the 42 "Inception" project. It defines a multi-container environment that delivers a production-style WordPress deployment with HTTPS termination, database, caching, administration, file transfer, Docker UI management, and a bonus static website. Every service is built from handcrafted Dockerfiles using Debian Bullseye (or closely related) base images, ensuring full ownership of the software stack and transparent reproducibility.
- Project Summary
- High-Level Topology
- Technology Stack
- Compose Orchestration
- Service Reference
- Configuration Files & Scripts
- Building & Running the Stack
- Security & TLS Notes
- Data Persistence & Host Bindings
- Troubleshooting Checklist
- Further Improvements
🌍 External Host (Your Browser, FTP Client, etc.)
────────────────────────────────────────────────
│
▼
┌────────────────────────────────────┐
│ NGINX (443) │
│------------------------------------│
│ • Reverse proxy for WordPress PHP │
│ • Handles HTTPS traffic │
│ • Accessible from your host │
└────────────────┬───────────────────┘
│
┌───────────────────────┴───────────────────────┐
│ │
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ WordPress (php-fpm) │ │ SITE (Static HTML) │
│────────────────────────│ │────────────────────────│
│ • Listens on port 9000 │ │ • Internal-only │
│ • Not exposed outside │ │ • Served via NGINX │
└───────────┬────────────┘ └────────────────────────┘
│
▼
┌────────────────────────┐
│ MariaDB (3306) │
│────────────────────────│
│ • Stores WP data │
│ • Internal-only │
│ • Connected via │
│ WORDPRESS_DB_HOST │
└───────────┬────────────┘
│
▼
┌────────────────────────┐
│ Redis (6379) │
│────────────────────────│
│ • Internal cache layer │
│ • Used by WP plugin │
└────────────────────────┘
┌──────────────────────────┐
│ FTP (21,21100+) │
│──────────────────────────│
│ • Accessible externally │
│ • Linked to /var/www/html│
└──────────────────────────┘
┌────────────────────────┐
│ Adminer (8080) │
│────────────────────────│
│ • External DB browser │
│ • Accessible via web │
└────────────────────────┘
┌──────────────────────────────┐
│ Portainer (9000,9443) │
│──────────────────────────────│
│ • Docker management UI │
│ • Uses /var/run/docker.sock │
└──────────────────────────────┘
Shared Docker Network: `inception`
────────────────────────────────────────────────────────────────
All containers can reach each other by name (DNS resolution).
Example: `wordpress` can reach `mariadb` using `DB_HOST=mariadb`
────────────────────────────────────────────────────────────────
The ASCII diagram above is duplicated from overview.txt so that the README remains self-contained.
The stack intentionally avoids pre-built service images and instead provisions every component manually, forcing explicit knowledge of the tooling in play.
| Layer | Technology | Role | Key Details |
|---|---|---|---|
| Container Engine | Docker Engine & Docker Compose | Orchestration | Compose file is srcs/docker-compose.yml; all images are built from local Dockerfiles. |
| Base OS | Debian Bullseye | Common base image | Provides a minimal Linux environment for all custom images except the static site, which builds from nginx:bullseye. |
| Reverse proxy | NGINX | TLS terminator and routing | Serves HTTPS traffic, forwards PHP requests to WordPress, and proxies /bonus/ to the static site. |
| Application runtime | PHP 7.4 (php-fpm) | WordPress execution | Provided via Debian packages; managed by php-fpm for better process isolation. |
| CMS | WordPress | Blogging platform | Automatically bootstrapped via wp-cli. |
| Database | MariaDB 10.x | WordPress relational store | Initialized with custom script that creates users and schema. |
| Cache | Redis | Object cache | Enabled via the Redis WordPress plugin for application-level caching. |
| File transfer | vsftpd | FTP server | Provides external access to the WordPress document root for file management. |
| DB admin | Adminer | Web-based DB UI | Runs on PHP's built-in web server and connects to MariaDB. |
| Docker management | Portainer | UI for Docker | Offers browser-based management of local containers. |
| Static site | NGINX (official image) | Bonus content | Ships a simple static HTML portfolio and is only reachable through the /bonus/ path on the main NGINX server. |
| SSL tooling | OpenSSL | Certificate generation | Self-signed certificates are created at container startup. |
| Automation | GNU Make, shell scripts | Developer ergonomics | The Makefile and build_script.sh provide repeatable workflows. |
All orchestration happens through srcs/docker-compose.yml. Every service block builds a local Dockerfile, declares networks, ports, volumes, restart strategies, and dependencies.
inception(bridge) – A user-defined bridge connecting every container. Docker's built-in DNS allows addressing services by container name (e.g.,wordpresscan reachmariadb:3306).
| Volume | Driver | Host binding | Usage |
|---|---|---|---|
mariadb_data |
local with type=none |
/home/tboussad/data/mariadb_data |
Persists MariaDB data directory across container restarts. |
wordpress_data |
local with type=none |
/home/tboussad/data/wordpress_data |
Stores WordPress core files, uploads, themes, and plugin state. |
Note: The hard-coded host paths match the 42 school Linux environment. Adjust
devicepaths if you run on another machine (e.g., map to${PWD}/data/...).
Several services load secrets and configuration from a shared .env file (referenced via env_file: .env). This file is intentionally ignored by git to avoid leaking credentials. Required keys are enumerated in Environment bootstrap.
Each service is built from a bespoke Dockerfile under srcs/requirements/.... This section documents exactly what happens during image build and runtime initialization.
- Context:
srcs/requirements/nginx - Base image:
debian:bullseye - Packages installed:
nginx,openssl(for self-signed TLS certificates). - Configuration:
conf/nginx.confplaces a single server block on port 443 with TLS 1.3 enforced.- It sets
root /var/www/wordpress;and forwards PHP requests towordpress:9000viafastcgi_pass. - Requests under
/bonus/are proxied to the static site container (http://site:80/).
- Entrypoint script:
tools/nginx_setup.sh- Creates
/etc/ssl/privateand/etc/ssl/certs. - Generates a self-signed certificate valid for 365 days using OpenSSL (
rsa:2048key). - Runs
nginx -g "daemon off;"so the container remains in the foreground.
- Creates
- Ports:
443exposed to the host. - Volumes: Shares
wordpress_dataas read-only web root. - Dependencies: Waits for
wordpressandsiteto be ready (declared viadepends_on).
- Context:
srcs/requirements/wordpress - Base image:
debian:bullseye - Packages installed:
php,php-fpm,php-mysql,php-redis,curl,unzip,netcat.php-redisenables WordPress to communicate with Redis.curlandunzipallow acquiring and unpacking WordPress core.
- Additional artifacts:
- Downloads
wp-cliPhar into/usr/local/bin/wpfor headless administration. - Custom PHP-FPM pool configuration
www.confsets the listener to port 9000 and uses thewww-datauser. - Entrypoint script
tools/wordpress_entrypoint.shorchestrates installation.
- Downloads
- Entrypoint workflow:
- Downloads WordPress core into
/var/www/wordpressif missing. - Applies secure ownership (
www-data) and permissions. - Generates
wp-config.phpusing values from the.envfile (database credentials and host). - Performs
wp core installto provision the site, specifying admin user/email plus an additional editor account. - Installs and activates the
redis-cacheplugin, injects Redis settings intowp-config.php, and enables cache withwp redis enable. - Executes the container command (
php-fpm7.4 -F) to run PHP-FPM in the foreground.
- Downloads WordPress core into
- Ports: Exposes
9000internally (no host binding, it is consumed by NGINX over the Docker network). - Volumes: Mounts
wordpress_data:/var/www/wordpressfor persistence and FTP sharing. - Environment variables consumed:
WORDPRESS_DB_NAME,WORDPRESS_DB_USER,WORDPRESS_DB_PASSWORD,WORDPRESS_DB_HOSTWP_SITE_URL,WP_SITE_TITLEWP_ADMIN_USER,WP_ADMIN_PASSWORD,WP_ADMIN_EMAILWP_EDITOR_USER,WP_EDITOR_PASSWORD,WP_EDITOR_EMAIL
- Context:
srcs/requirements/mariadb - Base image:
debian:bullseye - Packages installed:
mariadb-server. - Configuration:
conf/mariadb.confoverrides defaults to listen on every interface (0.0.0.0) on port 3306.- The Dockerfile prepares data directories (
/run/mysqld,/var/lib/mysql) and assigns appropriate ownership.
- Entrypoint script:
tools/mariadb_setup.sh- Ensures secure permissions (
chmod 700 /var/lib/mysql). - Detects an empty data directory and runs
mysql_install_dbon first boot. - Launches MariaDB in the background using
mysqld_safe, waits for readiness viamysqladmin ping. - Executes SQL to create the WordPress database, a regular user (
$DB_USER/$DB_PASS), and an elevated superuser ($DB_SUPER/$DB_SUPER_PASSWORD). - Flushes privileges and gracefully stops the temporary instance before starting the final foreground
mysqld_safeprocess.
- Ensures secure permissions (
- Ports: 3306 (not published to host).
- Volumes:
mariadb_data:/var/lib/mysql. - Healthcheck: Uses
mysqladmin pingevery 10 seconds with a 5-second timeout to ensure readiness for dependent services. - Environment variables consumed:
DB_NAME,DB_USER,DB_PASS,DB_SUPER,DB_SUPER_PASSWORD.
- Context:
srcs/requirements/bonus/redis - Base image:
debian:bullseye - Packages installed:
redis-server. - Configuration:
conf/redis.confdisables protected mode (allowing network connections inside the Docker network), listens on all interfaces, enforces a 256MB memory limit, and selectsallkeys-lrueviction to maintain cache efficiency.
- Command: Runs
redis-server /etc/redis/redis.conf. - Ports: 6379 (internal only).
- Purpose: Provides a fast, in-memory cache that the WordPress Redis plugin uses for object caching.
- Context:
srcs/requirements/bonus/ftp - Base image:
debian:bullseye - Packages installed:
vsftpd. - Configuration:
conf/vsftpd.confenables local user login, write access, chroot jail, and passive mode across ports 21100–21110.- Passive address is set to
0.0.0.0so Docker handles NAT translation.
- Entrypoint script:
tools/ftp_setup.sh- Ensures the secure chroot directory exists.
- Creates a system user (
$FTPUSER) whose home directory is/var/www/wordpress, aligning FTP operations with the WordPress document root shared viawordpress_data. - Sets the user password from
$FTPPASSand takes ownership of the WordPress files for upload capability. - Starts
vsftpdwith the custom configuration.
- Ports:
- Control channel:
21:21(host exposed). - Passive range:
21100-21110mapped directly for clients behind firewalls.
- Control channel:
- Volumes:
wordpress_data(shared with WordPress container). - Environment variables consumed:
FTPUSER,FTPPASS.
- Context:
srcs/requirements/bonus/adminer - Base image:
debian:bullseye - Packages installed:
php,php-mysqli,curl. - Build steps: Downloads the latest Adminer PHP single-file application into
/var/www/adminer/index.php. - Runtime: Executes
php -S 0.0.0.0:8080 -t /var/www/adminer, leveraging PHP's built-in development server (sufficient for a bounded admin tool inside Docker). - Ports:
8080:8080so the tool is reachable from the host. - Usage: Use your browser to connect to
https://localhost:8080(or the mapped host) and log into the MariaDB instance at hostmariadbwith the credentials specified in.env.
- Context:
srcs/requirements/bonus/portainer - Base image:
debian:bullseye - Packages installed:
curl,ca-certificates,tar. - Build steps:
- Downloads the Portainer Community Edition binary tarball (
2.21.3) directly from GitHub. - Extracts contents to
/opt/portainerand removes the archive.
- Downloads the Portainer Community Edition binary tarball (
- Runtime: Launches
/opt/portainer/portainer. - Ports:
9443:9443(HTTPS UI)9000:9000(legacy HTTP UI)
- Volumes: Binds
/var/run/docker.sockso Portainer can manage the Docker engine on the host. This grants Docker root-level access—handle with care.
- Context:
srcs/requirements/bonus/site - Base image:
nginx:bullseye - Packages: Updates the base image via
apt update && apt upgrade -y. - Content: Copies
tools/site.htmlas/usr/share/nginx/html/index.html. The HTML file is a minimalist personal landing page. - Runtime: Runs the default
nginxforeground command. - Ports: Exposes port 80 internally (no direct host binding; consumed by the main NGINX service under
/bonus/).
| Path | Purpose |
|---|---|
overview.txt |
ASCII network topology diagram replicated in this README. |
Makefile |
Defines automation targets: build, up, down, rebuild. Uses docker compose -f srcs/docker-compose.yml. |
build_script.sh |
Convenience script that calls make rebuild, then prints logs for each container sequentially. |
srcs/docker-compose.yml |
Central orchestration manifest. |
srcs/requirements/<service>/Dockerfile |
Service-specific image definitions. |
srcs/requirements/<service>/conf/* |
Runtime configuration files (e.g., nginx.conf, mariadb.conf, vsftpd.conf, redis.conf). |
srcs/requirements/<service>/tools/* |
Entrypoint scripts or static assets (WordPress entrypoint, nginx_setup.sh, etc.). |
srcs/requirements/wordpress/www.conf |
Custom PHP-FPM pool configuration. |
srcs/requirements/bonus/site/tools/site.html |
Static HTML served at /bonus/. |
.gitignore |
Added in this commit; keeps secrets, cache files, and runtime artefacts out of version control. |
All commands assume Docker Engine 24.x+ and Docker Compose plugin 2.x+ are installed.
- Build images
make build
- Start (rebuild + run in background)
The
make up
--buildflag in the Makefile guarantees images stay in sync with Dockerfiles. - Stop everything
make down
- Full rebuild (stop, rebuild, start)
make rebuild
Create a .env file in the repository root before launching Compose. Below is a comprehensive template covering every variable consumed across services:
# MariaDB
DB_NAME=wordpress
DB_USER=wp_user
DB_PASS=wp_password
DB_SUPER=wp_super
DB_SUPER_PASSWORD=wp_super_password
# WordPress core
WORDPRESS_DB_NAME=${DB_NAME}
WORDPRESS_DB_USER=${DB_USER}
WORDPRESS_DB_PASSWORD=${DB_PASS}
WORDPRESS_DB_HOST=mariadb
WP_SITE_URL=https://localhost
WP_SITE_TITLE=My Inception Site
WP_ADMIN_USER=admin
WP_ADMIN_PASSWORD=change_me_admin
WP_ADMIN_EMAIL=admin@example.com
WP_EDITOR_USER=editor
WP_EDITOR_PASSWORD=change_me_editor
WP_EDITOR_EMAIL=editor@example.com
# FTP access (shares wordpress_data volume)
FTPUSER=ftpuser
FTPPASS=change_me_ftpFeel free to extend this file with more service-specific variables if you add new features.
The helper script ./build_script.sh rebuilds the stack and prints logs from every container with sleep intervals for readability. For ad-hoc checks you can still run docker logs <service> manually.
- Certificates generated by
nginx_setup.share self-signed. Browsers will show a warning unless you import the certificate or replace it with one generated via a trusted CA (e.g., Let’s Encrypt). Autogeneration keeps builds deterministic but is insecure for production use. - Portainer mounts the Docker socket, granting administrative control over the host. Restrict access to the Portainer UI and rotate the admin password after first login.
- FTP transmits credentials in plaintext. Consider replacing it with SFTP (over SSH) if you need secure file transfer in production.
- Database credentials reside in
.env; treat the file as secret material and never commit it (the new.gitignoreenforces that). - Redis
protected-modeis disabled, so ensure the Docker network remains private.
mariadb_dataandwordpress_datavolumes bind into/home/tboussad/data/...to satisfy 42 school grading scripts. On other systems you can customize these paths (editdeviceindocker-compose.yml) to live under the repository, such as${PWD}/data/mariadb.- The WordPress directory is deliberately shared with the FTP container so web uploads appear instantly for FTP users.
- MariaDB data directory persists between runs. If you need a clean slate, delete the host directory and restart the stack.
- Containers restart continuously – Run
docker compose -f srcs/docker-compose.yml logs <service>to inspect stack traces. For database issues verify.envcredentials and confirm the host path is writable. - MariaDB fails healthcheck – Ensure your host machine isn’t blocking port 3306 internally and that the volume directory has correct permissions.
- WordPress setup hangs – Confirm Redis and MariaDB containers are reachable (
docker exec -it wordpress ping mariadb). Also verify DNS resolution through theinceptionnetwork. - FTP passive mode fails – Open firewall ports 21100–21110 or adjust the passive range in
vsftpd.conf. - Browser warns about certificate – Import
/etc/ssl/certs/nginx.crtfrom inside the NGINX container or configure a trusted certificate. - Portainer cannot connect to Docker – Ensure
/var/run/docker.sockexists on the host and Docker is running with sufficient permissions.