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, /healthz endpoints, and docker 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 after docker compose up.

Compose highlights

services:
api:
build: ./node-service
env_file: .env.api
depends_on: [db]
ports: ["4000:4000"]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4000/healthz"]
interval: 30s
timeout: 5s
retries: 3
python:
build: ./python-service
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
  • Why: Health checks keep Compose from routing traffic to sick containers. Named volumes preserve data. .env keeps secrets out of Git.
  • Observability: Logs include traceId so I can follow a request from Node → Python. When I tail logs, it’s clear which container said what.

Experiments & results

ExperimentObservationOutcome
Remove Express dependency mid-runOnly the Node build failed; Python kept serving other requestsProof that isolation works.
Force Python infinite loopNode flagged the dependency timeout but stayed healthyNeed better circuit breakers, but UI didn’t crash.
Mount log volume (./logs:/app/logs)Restarting containers kept historical logsGood 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, and scripts/cleanup.sh keep routines consistent.
  • Documentation: README.md covers setup, health checks, and TODOs; docs/runbook.md logs drills (failures, fixes, lessons).
  • Security: Multi-stage builds, npm audit, and pip-audit run 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.example files.

Links