This project has been created as part of the 42 curriculum by mkhavari
Inception is a Docker automation project that sets up a complete web infrastructure using containerized services. It demonstrates container orchestration, networking, persistence, and security best practices.
To build and manage a multi-container f environment with:
- Nginx as a reverse proxy (HTTPS/SSL)
- WordPress as the main web application
- MariaDB as the database
- Redis for caching
- FTP for file transfer
- Adminer for database management
- Portainer for container management
- React Portfolio as a bonus frontend application
All services communicate via Docker networks, with persistent data in Docker volumes or bind mounts.
Docker is a platform for building, packaging, and running applications inside lightweight, isolated environments called containers.
It allows developers to bundle their code, dependencies, and runtime into a portable unit that behaves the same on any machine.
Docker provides:
-
Images → reusable blueprints for applications
-
Containers → isolated runtime environments created from images
-
Volumes → persistent storage outside the container
-
Networks → virtual communication between containers
With Docker, you can run applications consistently across development, testing, and production without worrying about OS differences or dependency conflicts.
- VMs are an older way to package applications.
- Docker is a newer way to package applications.
- Both are virtualization tools.
- A VM virtualizes the OS (including the kernel) and the application layer.
- Docker virtualizes the application layer and shares the host OS kernel.
######## Virtualized ################
#-----------------------------------#
# application layer #
#-----------------------------------#
#####################################
-------------------------------------
| Kernel OS |
-------------------------------------
| HardWare |
-------------------------------------
######## Virtualized #################
# ---------------------------------- #
# application layer #
#------------------------------------#
# Kernel OS #
# ---------------------------------- #
######################################
-------------------------------------
| HardWare |
-------------------------------------
- VM is large (can be GBs) because it has its own OS kernel.
- Docker is lightweight because it includes only the application layer and shares the host OS kernel.
A Docker image is a blueprint for creating containers. It is built from a build context (your project folder) using a Dockerfile.
-
An image contains:
-
Your application code (copied during docker build)
-
Dependencies (Mariadb, Python, etc.)
-
System libraries
-
A minimal filesystem (user‑space only)
-
Instructions on how to run the app (CMD, ENTRYPOINT)
Images are read‑only and reusable.
- It is a way to package an application with all necessary dependencies and configuration.
- The package is portable and can be easily shared and moved around.
- It makes development and deployment more efficient.
- A container is a running instance of an image.
-
A container has:
-
Its own processes (Node, MongoDB, etc.)
-
Its own network namespace (IP, ports)
-
Its own environment variables
-
Its own isolated filesystem (based on the image) docker exec -it bash
But it shares the host’s kernel, which makes it lightweight.$> ls app boot etc lib media opt root sbin sys usr bin dev home lib64 mnt proc run srv tmp var
-
A volume is persistent storage outside the container.
Used for:
-
Databases (MongoDB, MySQL)
-
Uploaded files
-
Logs
-
Anything that must survive container deletion
If a container is removed, the volume stays and can be reused.
Image => Container
you can build unlimited container from the same Image
A container uses:
-
Host kernel (shared)
-
Host CPU
-
Host RAM
-
Host disk (for volumes)
-
Host network (via port mapping)
Everything else is isolated.
A container’s skeleton is:
- The image filesystem (root FS)
- The process namespace
- The network namespace
- The mount namespace
- The cgroup limits (CPU, RAM)
Together, these create a lightweight “mini‑OS” environment.
- It lives in a container repository/registry:
- Public registry (e.g., Docker Hub: https://hub.docker.com/)
- Private registry
- The installation process for each developer differed depending on the operating system.
- Everyone had to follow a different setup process even when working on the same project (e.g., if an application needs 10 services, each developer must install/configure those 10 services on their own OS).
- You don't need to install all those services directly on your operating system.
- Everything is packaged with the required configuration.
- One command is enough instead of running many different installation commands.
- Developers and Operations work together to package the application into one container.
- No environment configuration is needed (except Docker at runtime).

-
It is made up of images, which are stacked layers on top of each other.
-
The base layer is a Linux distribution such as Alpine.
-
It is important that the base layer is small, because it keeps the container size small.
--------------------- | Application | --------------------- | ... | --------------------- | intermidate Layer | --------------------- | Linux:alpine3.10 | ---------------------
| Aspect | Image | Container |
|---|---|---|
| Definition | A template (blueprint) used to create containers | A running instance created from an image |
| Nature | Static, read-only | Dynamic, read-write |
| State | Immutable (does not change after build) | Mutable (can change during execution) |
| Creation Time | Built during image build process | Created when the image is executed |
| Lifecycle | Exists as a file on disk or in a registry | Runs as a process; removed unless persisted |
| Purpose | Provides application environment and dependencies | Executes the application inside the environment |
| Analogy | Recipe for a cake | The actual baked cake |
| Aspect | Secrets | Environment Variables |
|---|---|---|
| Security | Sensitive data encrypted, passed via filesystem | Visible in ps, env, logs |
| Use Case | Passwords, API keys, sensitive credentials | Configuration, non-sensitive settings |
| Visibility | Only readable by containers with secret access | Visible to all processes in container |
| In Project | Used for: db_password, wp_admin_password, ftp_password |
Used for: DOMAIN_NAME, WORDPRESS_DB_HOST |
| Implementation | WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password |
DOMAIN_NAME: ${DOMAIN_NAME} |
Design Choice: This project uses Docker Secrets for all sensitive credentials (passwords) and Environment Variables for non-sensitive configuration (hostnames, usernames).
| Aspect | Docker Network (inception) | Host Network |
|---|---|---|
| Isolation | Containers isolated, communicate via network | Containers share host network stack |
| Communication | Service-name DNS resolution (e.g., mariadb:3306) |
Direct access to host ports |
| Security | Better isolation between services | Exposes all containers to host |
| Port Conflicts | No conflicts (each container has own namespace) | Risk of port conflicts |
| Default Bridge | Custom inception bridge network |
network_mode: host |
| In Project | ✅ networks: - inception (correct) |
❌ NOT USED (would fail evaluation) |
Design Choice: This project uses a custom Docker bridge network (inception) for secure container communication, preventing direct exposure to the host network.
| Aspect | Docker Volumes | Bind Mounts |
|---|---|---|
| Location | Managed by Docker (/var/lib/docker/volumes/) |
Any location on host filesystem |
| Performance | Optimized for Docker, faster | Depends on host filesystem |
| Portability | Portable across machines | Tied to host filesystem structure |
| Permissions | Managed by Docker daemon | Host filesystem permissions apply |
| In Project | mariadb:/var/lib/mysql (database persistence) |
./requirements/bonus/static/portfolio:/app (React dev) |
| Use Case | Production databases, generated data | Development, source code, config files |
Design Choice: This project uses:
- Docker Volumes for persistent database data (
mariadbvolume,wordpressvolume) - Bind Mounts for development (React portfolio source code for live reload)
- Volumes for Portainer data (
portainer_data)
- Docker and Docker Compose installed
- Make installed
- Linux system (tested on Debian 13)
- Domain name in
/etc/hosts:127.0.0.1 mkhavari.42.fr
git clone <repo-url>
cd inceptionCopy the .env.example to .env and update variables:
cp srcs/.env.example srcs/.env
# Edit srcs/.env with your domain, usernames, etc.make allThis will:
- Add domain to
/etc/hosts - Create data directories
- Generate random secrets
- Build all Docker images
- Start all containers
| Service | URL | Credentials |
|---|---|---|
| WordPress | https://mkhavari.42.fr |
User: mkhavari / Pass: see secrets/wp_admin_password.txt |
| WordPress Admin | https://mkhavari.42.fr/wp-admin |
Same as above |
| Adminer | http://localhost:8080/adminer.php |
User: wp_user, Host: mariadb, DB: wordpress |
| Portainer | https://localhost:9443 |
Create on first login |
| React Portfolio | http://localhost:5173 |
Direct access |
| FTP | localhost:21 |
User: ftpuser / Password: in secrets/ftp_password.txt |
make ps # List running containers
make logs # View container logs
make restart # Restart all services
make stop # Stop all services
make start # Start stopped services
make down # Stop and remove containers
make clean # Remove containers, images, volumes
make fclean # Full clean + remove data directories
make re # Full rebuild from scratch| Operation | Meaning | Command |
|---|---|---|
| Create | Build a new image from a Dockerfile | docker build -t <name>:<tag> . |
| Read | List all local images | docker images |
| Update | Rebuild image (use --no-cache to refresh layers) |
docker build --no-cache -t <name>:<tag> . |
| Delete | Remove an image | docker rmi <image_id> |
| Delete (Force) | Remove image even if used by a container | docker rmi -f <image_id> |
| Export | Save image to a tar archive | docker save -o <file>.tar <image> |
| Import | Load image from a tar archive | docker load -i <file>.tar |
| Prune | Remove all dangling images | docker image prune |
| Operation | Meaning | Command |
|---|---|---|
| Create | Create container (don't start) | docker create --name <name> <image> |
| Create + Run | Create and start container (detached) | docker run -d --name <name> <image> |
| Read (Running) | List only running containers | docker ps |
| Read (All) | List all containers (including stopped) | docker ps -a |
| Update (Start) | Start a stopped container | docker start <name_or_id> |
| Update (Restart) | Restart a container | docker restart <name_or_id> |
| Update (Exec) | Run a command inside a running container | docker exec -it <name_or_id> bash |
| Delete (Stop) | Stop a running container | docker stop <name_or_id> |
| Delete (Remove) | Remove a stopped container | docker rm <name_or_id> |
| Delete (Force) | Force stop and remove container | docker rm -f <name_or_id> |
| Logs | View container logs (follow) | docker logs -f <name_or_id> |
| Prune | Remove all stopped containers | docker container prune |
Run this in the folder containing your Dockerfile:
docker build -t client-image .This:
- Reads your Dockerfile
- Copies your code into the image
- Installs dependencies
- Produces a reusable image
docker run -d -p 5000:5000 --name my-app-container my-app-imageExplanation:
- -d → run in background
- -p 5000:5000 → hostPort:containerPort
- --name → readable container name
client-image → the image you built
| Test | Command | Expected Result |
|---|---|---|
| Connection status | docker exec -it wordpress wp redis status --allow-root --path=/var/www/html |
Status: Connected |
| Key count (before visit) | docker exec -it redis redis-cli dbsize |
0 |
| Key count (after visit) | docker exec -it redis redis-cli dbsize |
> 0 (cache populated) |
| Live monitor | docker exec -it redis redis-cli monitor |
Shows GET/SET on browser request |
| List cached keys | docker exec -it redis redis-cli keys "*" |
wp_:options, wp_:posts, etc. |
| Read cached value | docker exec -it redis redis-cli get "wp_:options:siteurl" |
"https://mkhavari.42.fr" |
test wordpress has connection to maraidb: docker exec -it wordpress bash -c 'mariadb -hmariadb -u wp_user -p"$(cat /run/secrets/db_password)" -e "SELECT 1;"'
docker compose build --no-cache wordpress docker compose up
- This file contains environment variable definitions for configuring a WordPress instance.
- WORDPRESS_DB_NAME: Specifies the name of the WordPress database.
- WORDPRESS_DB_USER: Defines the username used to connect to the database.
- WORDPRESS_DB_PASSWORD_FILE: Points to the file containing the database password for enhanced security.
- WORDPRESS_DB_HOST: Specifies the database host and port (e.g., hostname:port).
- Note: The database password is stored in an external file for security purposes.
Dockerfile is a blueprint to create environment images
FROM debian:bullseye
RUN apt-get update && apt-get install -y \
php7.4 \
php7.4-fpm \
php7.4-mysql \
mariadb-client \
wget \ # to download wordpress
curl \
tar \ # to extract wordpress tar
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p /var/www/html && \
wget https://wordpress.org/latest.tar.gz -O /tmp/wordpress.tar.gz && \
tar -xzf /tmp/wordpress.tar.gz -C /var/www/html --strip-components=1 && \
rm /tmp/wordpress.tar.gz # delete the download file no longer need, reduce the final image size
RUN curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \
&& chmod +x wp-cli.phar \
&& mv wp-cli.phar /usr/local/bin/wp
# Copy php-fpm config
COPY conf/www.conf /etc/php/7.4/fpm/pool.d/www.conf
# Copy entrypoint script
COPY tools/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
EXPOSE 9000
ENTRYPOINT ["/entrypoint.sh"]
CMD ["php-fpm7.4", "-F"]
for the first time the entrypoint will run when container start it will runs with ENTRYPOINT ["/entrypoint.sh"]
This script is a database bootstrapper: it prepares MariaDB the first time the container runs, creates the WordPress database and user, and then starts MariaDB in the foreground.
Browser requests: https://mkhavari.42.fr/wp-login.php
│
▼
Nginx receives it
│
Is it a .php file?
│
YES ─┘
│
▼
fastcgi_pass wordpress:9000
(sends the .php file path to PHP-FPM)
│
▼
PHP-FPM (www-data) executes the .php file
WordPress talks to MariaDB if needed
│
▼
PHP-FPM returns pure HTML back to Nginx
│
▼
Nginx sends HTML to Browser
https://mkhavari.42.fr
│
├── mkhavari ──► full admin access (manages everything)
│
├── javad ──► author access (writes posts only)
│
└── wp_user ──► NOT a WordPress user (only talks to MariaDB)
docker exec -it ps -ef or docker exec -it ps aux then you will see: PID COMMAND 1 nginx: master process nginx -g daemon off; 12 nginx: worker process
with command:
docker stop <name_container> => if stoped with delay them container PID is not triggerd by SIGTERM
or :
docker kill --signal=SIGTERM <name_container>
go in container: docker exec -it sh thne: for i in $(seq 1 5); do (sleep 1 &) done if you see: PID 1 is not reaping zombies → broken container.
access to the pointer: https://localhost:9443
access to wordplress admin ? address: https://mkhavari.42.fr/wp-login.php or https://mkhavari.42.fr/wp-admin admin user: mkhavari password secrets/wp_admin_password.txt
- Docker Documentation
- Docker Compose Specification
- Docker Best Practices
- Docker Networking
- Docker Volumes
- Dockerfile Reference
- Nginx Documentation
- WordPress Codex
- MariaDB Knowledge Base
- Redis Documentation
- vsftpd Manual
- Adminer Documentation
- Portainer Docs
1. Docker Configuration & Architecture
- Designing multi-service Docker Compose setup
- Defining optimal networking strategy (bridge network vs host)
- Planning volume/bind mount strategy for persistence and development
- Choosing appropriate base images (Debian vs Alpine)
2. Entrypoint Scripts
- Generating bash scripts for service initialization
- Implementing retry logic for service dependencies (MariaDB wait-for-readiness)
3. Nginx Configuration
- SSL/TLS certificate generation command
- PHP-FPM FastCGI proxy configuration
- URL rewriting rules for WordPress
- Security headers and best practices
4. React Portfolio Frontend
- Tailwind CSS styling for responsive design
- React hooks usage (useState, useEffect)
- Hot Module Reload (HMR) configuration in Vite
5. Documentation & README
- Explaining Docker concepts and comparisons
- Writing comprehensive README structure
- Creating CRUD operation tables
- Design choice explanations
- Instruction formatting and organization
Fully Implemented by Student:
- Project conception and planning
- Overall architecture decisions
- Dockerfile builds and customization
- Service integration and testing
- Debugging and troubleshooting
- Makefile automation
- Environment variable management
- Security decisions (Secrets vs Environment Variables)