A 42 Inception project that builds a small Docker-based web infrastructure from scratch inside a virtual machine.
This implementation uses Debian 11.11 as the base image for all three required services:
- NGINX with HTTPS/TLS
- WordPress with PHP-FPM
- MariaDB
The services are orchestrated with Docker Compose, connected through a dedicated bridge network, and persist their data through bind-mounted host directories under /home/$USER/data.
There is an additional version for Debian 12.11 in the extras folder.
The goal of Inception is to learn how to design and manage a multi-container setup without relying on preconfigured full-service images for the main application stack.
In this project:
- NGINX is the only service exposed to the host
- WordPress runs separately with PHP-FPM
- MariaDB runs in its own dedicated container
- WordPress and database data are stored in persistent bind-mounted volumes
- Environment variables are loaded from a
.envfile - The stack is controlled through a root
Makefile
Browser
|
v
NGINX (443, TLS)
|
v
WordPress + PHP-FPM (9000)
|
v
MariaDB (3306, internal only)
Each service is installed from Debian:11.11
Installs mariadb-server.
Its custom entrypoint script:
- validates required environment variables
- initializes the database only on first run
- creates the configured database
- creates the configured MariaDB user
- grants privileges
- sets MariaDB to listen on 0.0.0.0 for internal Docker network access
- starts the server with mysqld_safe
Initialization is guarded by a marker file:
/var/lib/mysql/.db_initialized
Installs:
- PHP-FPM 7.4
- required PHP extensions
- mariadb-client
- wp-cli
Its entrypoint script:
- configures PHP-FPM to listen on port 9000
- waits until MariaDB becomes reachable
- downloads and extracts WordPress on first mount
- generates wp-config.php
- injects database credentials from environment variables
- adds WordPress salts
- installs the site using wp-cli
- creates one administrator account and one additional author user
- fixes ownership and file permissions
- starts PHP-FPM in the foreground
WordPress initialization is guarded by:
/var/www/html/.firstmount
Installs:
- nginx
- openssl
Its entrypoint script:
- waits briefly for WordPress
- generates a self-signed TLS certificate
- writes the NGINX virtual host configuration dynamically
- enables only HTTPS on port 443
- forwards PHP requests to the wordpress container on port 9000
- starts NGINX in the foreground
TLS is configured with TLSv1.2 and TLSv1.3
A dedicated bridge network is created. This allows the services to communicate internally by container name:
inception_network
Two bind-mounted volumes are used:
- mariadb_data -> ${HOME}/data/mariadb
- wordpress_data -> ${HOME}/data/wordpress
Inside the containers, they are mounted as:
- /var/lib/mysql
- /var/www/html
This ensures the database and WordPress files persist across rebuilds.
In the real submission the .env file was not included, but I added it here for convenience considering the fact that it does not actually contain any real sensitive info in this course project.
.env variables:
DOMAIN_NAME=
#mariadb configuration
MYSQL_USER=
MYSQL_DATABASE=
MYSQL_PASSWORD=
MYSQL_ROOT_PASSWORD=
#Wordpress configuration
WORDPRESS_TITLE=
WORDPRESS_ADMIN_USER=
WORDPRESS_ADMIN_PASSWORD=
WORDPRESS_ADMIN_EMAIL=
WORDPRESS_USER=
WORDPRESS_USER_PASSWORD=
WORDPRESS_EMAIL=
make # Start everything
make up # Same functionality
These will:
- verify Docker is running
- create the local bind-mount directories if needed
- build the images
- start the containers in detached mode
- prints the main site URL and the WordPress login URL
make build # Build images only
make clean # Clean containers
make re # Clean and rebuild
make fclean # Clean up everything created by the project
- fclean will also offer to remove /home/$USER/data/mariadb and /home/$USER/data/wordpress, which must be created to store data locally for this project, though care needs to be takes if testing this on a regular system, since these folders might already exist and contain other data.
Docker and Docker Compose installed.
The chosen domain should be added in /etc/hosts so that it resolves locally, for example using the default domain name in my .env:
Bash
127.0.0.1 arissane.42.fr
Then start the project with "make" inside the /Debian11.11 directory
Once containers are running, open:
https://(your-domain)
Or for the WordPress login page
https://(your-domain)/wp-login.php
Because the certificate is self-signed, the browser will show a warning. That is expected in this local setup.
If MariaDB keeps restarting or NGINX returns 403 Forbidden after recreating the volumes, check the permissions of:
/home/$USER/data/mariadb/home/$USER/data/wordpress
This project uses bind-mounted host directories, so incorrect permissions can prevent MariaDB from writing its database files or WordPress from extracting its site files correctly. Recreating the folders with proper permissions before running make usually fixes the issue.
A few implementation details specific to this repository:
- All three main services are built from Debian 11.11
- The stack uses custom entrypoint scripts to handle initialization logic
- MariaDB initialization only happens once
- WordPress installation only happens once per mounted site volume
- NGINX configuration is generated dynamically at container startup
- PHP requests are passed from NGINX to wordpress:9000
- Only port 443 is exposed to the host
- Persistent data is stored outside the containers in /home/$USER/data
This project helped me practice:
- building custom Docker images
- orchestrating multi-container applications with Docker Compose
- configuring HTTPS with NGINX and OpenSSL
- connecting services through Docker networks
- managing persistent data with bind mounts
- automating service initialization with shell scripts
- setting up WordPress with PHP-FPM and MariaDB
- using environment variables to configure a containerized stack

