Containerizing Multi-Language Projects with Docker: A Practical Guide
Context: Personal repo (
docker_multilang_project) where I containerized a Node API and a Python utility. No clients, no prod traffic—just me trying to stop “works on my machine” excuses.
AI assist: ChatGPT summarized Docker docs and suggested health-check patterns; I kept all final code/tests/manual notes in the repo.
Status: Student exercise. Useful for onboarding and demos, but not hardened for production.
Reality snapshot
- Stack: Node 20 + Express (
/api), Python 3.11 helper service, Postgres 15. Managed via Docker Compose v3.9. - Use cases: practice dependency isolation, log correlation, and failure drills.
- Observability: JSON logs,
/healthzendpoints, anddocker compose logs. No ELK/Splunk stack yet. - Hosting: Runs locally or on a single VM. Not deployed for real users.
Architecture overview
docker_multilang_project/├── docker-compose.yml├── node-service/│ ├── Dockerfile│ ├── package.json│ └── server.js├── python-service/│ ├── Dockerfile│ └── app.py└── scripts/└── smoke.sh
node-service: REST API that exposes math endpoints and proxies to the Python service when needed.python-service: Handles CPU-heavy calculations/string parsing. Communicates via HTTP on the internal network.scripts/smoke.sh: Quick smoke test hitting both services + Postgres afterdocker compose up.
Compose highlights
services:api:build: ./node-serviceenv_file: .env.apidepends_on: [db]ports: ["4000:4000"]healthcheck:test: ["CMD", "curl", "-f", "http://localhost:4000/healthz"]interval: 30stimeout: 5sretries: 3python:build: ./python-servicedb:image: postgres:15environment:POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}volumes:- pgdata:/var/lib/postgresql/datavolumes:pgdata:
- Why: Health checks keep Compose from routing traffic to sick containers. Named volumes preserve data.
.envkeeps secrets out of Git. - Observability: Logs include
traceIdso I can follow a request from Node → Python. When I tail logs, it’s clear which container said what.
Experiments & results
| Experiment | Observation | Outcome |
|---|---|---|
| Remove Express dependency mid-run | Only the Node build failed; Python kept serving other requests | Proof that isolation works. |
| Force Python infinite loop | Node flagged the dependency timeout but stayed healthy | Need better circuit breakers, but UI didn’t crash. |
Mount log volume (./logs:/app/logs) | Restarting containers kept historical logs | Good for debugging; README now reminds me to clean the directory. |
Scale Node tier (docker compose up --scale api=3) | Load balanced across three containers (still localhost traffic) | Helps me explain horizontal scaling during interviews. |
Tooling & docs
- Scripts:
scripts/new-cert.sh,scripts/smoke.sh, andscripts/cleanup.shkeep routines consistent. - Documentation:
README.mdcovers setup, health checks, and TODOs;docs/runbook.mdlogs drills (failures, fixes, lessons). - Security: Multi-stage builds,
npm audit, andpip-auditrun before merges. Findings get tracked in repo issues. - CI/CD: GitHub Actions builds the images, runs lint/tests, and publishes artifacts so I can pull them on another machine.
What still needs work
- CI smoke tests that spin up the stack in GitHub Actions and run
scripts/smoke.sh. Currently manual. - More realistic observability stack (Prometheus + Grafana or at least Loki) instead of plain logs.
- Automated cleanup for volumes/networks so teammates don’t end up with stale resources.
- Better story around secrets (maybe doppler/.env vault). For now I rely on
.env.examplefiles.
Links
- Repo: https://github.com/BradleyMatera/docker_multilang_project
- Smoke test script example: https://gist.github.com/BradleyMatera/docker-multilang-smoke
- Trivy scan notes:
docs/security.md