One container is not enough. We need to go deeper.
Inception is a comprehensive infrastructure-as-code project that demonstrates modern DevOps practices using Docker containerization and Ansible automation. The project creates a complete, production-ready web hosting stack with WordPress, MariaDB, Nginx, FTP, Redis, and Adminer services—all orchestrated through Docker Compose.
The stack consists of seven microservices running in isolated, production-grade containers:
- Nginx: Reverse proxy and web server with HTTPS/SSL
- WordPress: Content management system (auto-installed on first run)
- MariaDB: Relational database backend
- Redis: In-memory cache for performance
- GoBackup: Automated database backup service with web UI
- FTP Server: Secure file transfer with FTPS
- Adminer: Web-based database administration
WordPress Automatic Setup: On first run, WordPress is automatically installed with admin user, additional users, and Redis Object Cache plugin pre-configured.
1. Clone the repository:
git clone <repository-url> inception
cd inception2. Prepare host:
uv sync
ansible-playbook playbooks/site.yml3. Start the stack:
make upThis command:
- Generates random credentials for all services
- Builds Docker images
- Starts all containers
- Displays live logs
Press Ctrl+C to exit logs (containers continue running).
4. Access the services:
- Website: https://lrocca.42.fr/
- WordPress Admin: https://lrocca.42.fr/wp-admin/
- Database UI: https://adminer.lrocca.42.fr/
- Backups: https://gobackup.lrocca.42.fr/
- FTP: lrocca.42.fr:21
make up # Start all services (with logs)
make down # Stop all services
make build # Build images without starting
make logs # View service logs
make re # Rebuild and restart
make secrets # Generate credential files
cd srcs && docker compose ps # Check container statusFor detailed step-by-step instructions including:
- Python environment setup
- Configuration of project variables
- SSL certificate generation
- Secrets file creation
- Docker permission setup
See DEV_DOC.md.
For information about:
- Using the website and WordPress admin
- Accessing the database management UI
- Connecting via FTP
- Viewing and managing credentials
- Checking service health
See USER_DOC.md.
| Aspect | Virtual Machines | Docker |
|---|---|---|
| Startup time | 30-60 seconds | <1 second |
| Resource usage | High (full OS per service) | Low (shared kernel) |
| Isolation | Complete OS isolation | Process-level isolation |
| Portability | Hypervisor-dependent | Runs anywhere with Docker |
| Development-Production parity | Differences possible | Identical environments |
| Complexity | High management overhead | Simple, reproducible |
Why Docker for this project: We need rapid iteration, lightweight deployments, and consistency between development and production. Docker provides all of this while keeping resource usage minimal—critical for both local development and cloud deployment.
| Aspect | Environment Variables | Secrets |
|---|---|---|
| Security | Visible in process lists, logs | Not exposed to child processes |
| Audit trail | Hard to track changes | Managed via files with permissions |
| Secrets rotation | Requires restart | Can be reloaded |
| Accidental leaks | High risk (visible in logs) | Lower risk (file-based) |
| Development experience | Simple .env files |
Slightly more setup |
Why we use Docker Secrets: This approach prevents sensitive credentials from appearing in container logs, process listings, or accident Docker inspect output. Credentials are mounted as files with restrictive permissions (600), accessible only to the container at /run/secrets/. This follows Docker and security best practices.
Implementation:
secrets/db_password.txt- Generated randomly, mounted as Docker secretsecrets/db_root_password.txt- Database root credentialssecrets/ftp_password.txt- FTP authentication
| Aspect | Host Network | Docker Bridge Network |
|---|---|---|
| Isolation | No network isolation | Complete network isolation |
| Port conflicts | Easy to collide | Namespace prevents collisions |
| Service discovery | Only localhost | Docker DNS (service names work) |
| Production readiness | Not recommended | Industry standard |
| Security | Single network space | Segmented by container |
Why we use Docker Bridge Network: This provides service-to-service communication through hostnames (mariadb, nginx, etc.), prevents port conflicts, and isolates services from the host network. This is essential for:
- Running multiple services on the same machine
- Service discovery and dependency management
- Security isolation
Network topology:
- Services communicate internally via Docker bridge network
- Only Nginx (reverse proxy) exposes ports 80/443 to host
- FTP exposes ports 21, 21000-21010 to host
- Other services (MariaDB, Redis, Adminer) are not directly exposed
| Aspect | Bind Mounts | Docker Volumes |
|---|---|---|
| Management | Manual filesystem paths | Docker-managed |
| Portability | OS/path-dependent | Portable across systems |
| Performance | Slower on some OSes | Optimized by Docker |
| Backup | Manual copy operations | docker volume commands |
| Permissions | Host filesystem logic | Docker-managed |
| Best for | Config files, source code | Database and application data |
Why we use Volumes for data: Named volumes are managed by Docker, making them portable, easier to backup, and more secure. Database and WordPress files require persistence that survives container recreation.
Our implementation:
-
Named Volumes (Docker-managed, for application data):
inception_database- MariaDB data at/var/lib/mysqlinception_wordpress- WordPress files at/var/www/html/wordpress- Persist across restarts and container recreation
- Backed up with
docker volumecommands
-
Bind Mounts (file-system paths, for configuration):
ssl/lrocca.crtandssl/lrocca.key- SSL certificatessecrets/*.txt- Credentials (safer as Docker secrets option)- Visible in Git for version control
- Easier to review and modify
For more help, see USER_DOC.md (end users) or DEV_DOC.md (developers).