This document explains, in clear and simple terms, how a developer can set up, build, run, and maintain the Inception stack.
- Prerequisites
- Project layout
- Configuration and secrets
- Build and launch
- Manage containers and volumes
- Data persistence (where data lives)
- Verify and debug services
- Troubleshooting
- Related docs
- Linux host (as required by the project subject)
- Docker Engine + Docker Compose plugin
- GNU Make
Quick checks:
docker --version
docker compose version
make --versioninception/
├── Makefile
├── README.md
├── DEV_DOC.md
├── USER_DOC.md
├── secrets/ # local secret files (not committed)
│ ├── db_root_password.txt
│ ├── db_password.txt
│ ├── wp_admin_password.txt
│ └── wp_user_password.txt
└── srcs/
├── .env # non-sensitive configuration
├── docker-compose.yml # defines services, networks, volumes, and secrets
└── requirements/
├── mariadb/
│ ├── 50-server.cnf
│ ├── Dockerfile
│ └── mariadb.sh
├── nginx/
│ ├── config.conf
│ └── Dockerfile
└── wordpress/
├── config.conf
├── Dockerfile
└── wordpress.sh
Makefile: entrypoint for common operations (make,make down,make fclean, ...)srcs/docker-compose.yml: defines services, networks, volumes, and secretssrcs/requirements/: one subdirectory per service, each with its ownDockerfilesecrets/: one file per secret, mounted into containers under/run/secrets/srcs/.env: non-sensitive environment variables used by Docker Compose
- Domain / host mapping (
<login>.42.fr→127.0.0.1in/etc/hosts) - Non-sensitive values in
srcs/.env(database name, usernames, emails, domain, site title) - Sensitive passwords in
secrets/*.txt(one file per secret)
| Variable | Purpose |
|---|---|
MYSQL_DATABASE |
Database name for WordPress |
MYSQL_USER |
Non-root database user for WordPress |
WP_TITLE |
WordPress site title |
DOMAIN_NAME |
Domain name used by WordPress and NGINX |
WP_ADMIN_USER |
WordPress admin username |
WP_ADMIN_EMAIL |
WordPress admin email |
WP_USER |
Non-admin WordPress username |
WP_USER_EMAIL |
Non-admin WordPress user email |
| File | Purpose |
|---|---|
secrets/db_root_password.txt |
MariaDB root password |
secrets/db_password.txt |
MariaDB password for the WordPress DB user |
secrets/wp_admin_password.txt |
WordPress admin password |
secrets/wp_user_password.txt |
Secondary WordPress user password |
Secrets are stored as files and mounted into containers under /run/secrets/. In docker-compose.yml, the secrets: top-level section defines them and per-service secrets: entries grant access.
Run from the repository root.
makemake downmake remake fcleandocker compose psdocker compose logs -fdocker compose restart <service>docker compose build --no-cache
docker compose up -ddocker volume lsdocker volume inspect <volume_name>Container filesystems are ephemeral: if you recreate a container, its internal filesystem resets. To persist data, Docker uses volumes (or bind mounts).
- MariaDB data: database files should live on a Docker volume
- WordPress files: uploads and site files should live on a Docker volume
By default on Linux, Docker-managed volumes live under:
/var/lib/docker/volumes/
You can confirm the exact path with:
docker volume inspect <volume_name> | grep -i MountpointUse curl to verify that the domain is reachable over HTTPS and that the TLS handshake is working.
curl -v https://login.42.frExpected output:
* Trying 127.0.0.1:443...
* Connected to login.42.fr (127.0.0.1) port 443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (OUT), TLS alert, unknown CA (560):
* SSL certificate problem: self-signed certificate
* Closing connection 0
curl: (60) SSL certificate problem: self-signed certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.This output confirms that NGINX is running and accepting connections on port 443, and that it is presenting a certificate during the TLS handshake.
The error from self-signed certificate is expected in a local/offline setup, an internet connection is needed to obtain a valid certificate from a trusted CA (like Let's Encrypt) and this project is only run locally. More details here: About self-signed certificates.
To bypass the certificate trust check and see the actual response:
curl -vk https://login.42.frConfirm HTTP is blocked:
curl -v http://login.42.frAs HTTPS is enforced, the HTTP request should fail with a connection error:
* Trying 127.0.0.1:80...
* connect to 127.0.0.1 port 80 failed: Connection refused
* Failed to connect to login.42.fr port 80 after 0 ms: Couldn't connect to server
* Closing connection 0
curl: (7) Failed to connect to login.42.fr port 80 after 0 ms: Couldn't connect to serverNo handshake occurs because NGINX is not listening on port 80 at all. The connection is refused at the TCP level, before any HTTP or TLS exchange can happen. This confirms that HTTPS is properly enforced and HTTP access is fully blocked.
TLSv1.2 and TLSv1.3 must be accepted, and older versions must be rejected.
openssl s_client -connect login.42.fr:443 -servername login.42.fr -tls1_2 # should connect
openssl s_client -connect login.42.fr:443 -servername login.42.fr -tls1_3 # should connect
openssl s_client -connect login.42.fr:443 -servername login.42.fr -tls1_1 # should fail
openssl s_client -connect login.42.fr:443 -servername login.42.fr -tls1 # should failA TLS certificate serves two purposes: encrypting traffic and proving the server's identity to a trusted third party (a Certificate Authority, or CA). Publicly trusted CAs (like Let's Encrypt) only issue certificates for domain names that are publicly reachable and DNS-verified. Since login.42.fr is a local domain that exists only in /etc/hosts and resolves to a VM IP, no public CA can verify it. A self-signed certificate is the only practical option in this context. This does not weaken the encryption. The TLS handshake still runs, session keys are still negotiated, and all traffic between the browser and NGINX is still encrypted. What is missing is third-party identity verification which is irrelevant here because you control both the client and the server. The browser warning is a UX safeguard designed for public internet users, not for local infrastructure. In production, you would use a CA-signed certificate (e.g. via Let's Encrypt) so that browsers trust the server without a manual exception.
Enter the MariaDB container:
docker exec -it srcs-mariadb-1 bashThen connect to MariaDB:
mysql -u root -pYou can verify WordPress tables exist:
SHOW DATABASES;
USE <your_database_name>;
SHOW TABLES;Check main process (PID 1) is PHP:
docker exec srcs-wordpress-1 cat /proc/1/cmdline
php-fpm: master process (/etc/php/8.2/fpm/php-fpm.conf)Confirm NGINX is not installed in the WordPress container:
docker exec srcs-wordpress-1 which nginxCheck running processes:
docker exec srcs-wordpress-1 ps auxShow WordPress directory structure:
docker exec srcs-wordpress-1 ls /var/www/wordpressCheck what process is PID 1:
docker exec srcs-mariadb-1 cat /proc/1/cmdlineCheck running processes:
docker exec srcs-mariadb-1 ps auxConfirm NGINX is not installed in the MariaDB container:
docker exec srcs-mariadb-1 which nginxList volume names:
docker volume lsInspect a volume:
docker volume inspect <volume_name>- Stop and start again:
make down
make- Visit
https://login.42.frand confirm your content is still present.
- Ensure your domain resolves to your local machine (often by editing
/etc/hosts). - Verify NGINX is listening and certificates exist inside the container.
- First boot scripts typically run only when the DB volume is empty.
- If you change DB credentials after initialization, you may need to
make fcleanand start again.
- End-user actions:
USER_DOC.md - Project overview and design notes:
README.md