Skip to main content
· Containers · 4 min read

Teaching Infrastructure Thinking Through Production-Ready Bots

When onboarding juniors, the hardest shift isn’t teaching them syntax — it’s helping them think in terms of systems that survive contact with production. Filesystem permissions, process supervision, network boundaries, CI/CD pipelines. These concepts don’t appear in tutorials.

The bridge I use: chatbots as a training ground.

Why Chatbots Work as Learning Projects

A Discord or Telegram bot is deceptively simple:

  • It reads input → processes it → writes output
  • But to do it properly, you need:
    • Environment isolation (Docker)
    • Persistent configuration (environment variables, not hardcoding)
    • Background process supervision (systemd or a proper runner)
    • Scheduled tasks (reminders, health checks)
    • External API integrations (webhooks, third-party services)
    • Testing before deployment

This is the same mental model as operating any distributed service.

The Progression

%%{ init: { 'look': 'handDrawn' } }%%
graph LR
    A[Local Bot<br/>Python script] --> B[Dockerize<br/>Dockerfile]
    B --> C[VPS Deployment<br/>docker-compose]
    C --> D[Add Health Checks<br/>Monitoring]
    D --> E[CI/CD Pipeline<br/>GitHub Actions]
    E --> F[Message Queue<br/>RabbitMQ]
    F --> G[Production<br/>Kubernetes]

Each stage introduces one operational concept without overwhelming the learner.

Stage 1: Local script that works

Start with a single Python file. Read from a file. Write to a file. Schedule with time.sleep(). This works — but it has no error handling, no restart policy, no way to inspect it.

Stage 2: Dockerize it

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "bot.py"]

Now the bot runs identically on the developer’s machine and on any server. Environment parity — the first DevOps concept.

Stage 3: Deploy to a VPS

docker-compose.yml on a $5 VPS:

services:
  bot:
    build: .
    restart: unless-stopped
    environment:
      - BOT_TOKEN=${BOT_TOKEN}
    volumes:
      - ./data:/app/data

restart: unless-stopped is process supervision without systemd complexity. The bot survives reboots.

Stage 4: Add a health check endpoint

Even a simple GET /health that returns 200 OK enables:

  • Load balancer health probes
  • External monitoring (UptimeRobot, Grafana)
  • Alerting when the bot goes down

Stage 5: CI/CD pipeline

%%{ init: { 'look': 'handDrawn' } }%%
graph TD
    A[Push to main] --> B[GitHub Actions]
    B --> C[Run Tests]
    C --> D[Build Docker Image]
    D --> E[Push to Registry]
    E --> F[SSH to VPS]
    F --> G[docker-compose pull]
    G --> H[docker-compose up -d]

GitHub Actions runs tests, builds the image, then SSHs into the VPS to pull and restart. No secrets stored on the server — everything via GitHub Secrets.

Stage 6: Extract workers into RabbitMQ

When the bot needs to handle bursty load, move long-running tasks to a queue:

%%{ init: { 'look': 'handDrawn' } }%%
graph LR
    A[Bot receives request] --> B[Publish to RabbitMQ]
    B --> C[Worker pool<br/>picks up job]
    C --> D[Process in background]
    D --> E[Publish result back]
    E --> F[Bot responds to user]

The bot becomes a lightweight API. Workers scale independently. This is also the stage where the learner stops thinking about “my program” and starts thinking about a system with parts that fail separately — why queues are an operational tool, not just a backend pattern.

What This Teaches

ConceptWhere it appears
Environment parityDocker
Process supervisionrestart: unless-stopped
Configuration as codedocker-compose.yml
Secrets managementEnvironment variables, GitHub Secrets
MonitoringHealth check endpoints
CI/CDGitHub Actions pipeline
ScalabilityMessage queues, worker pools
Infrastructure-as-codeVPS provisioning (even if manual)

The goal isn’t to teach Docker or RabbitMQ specifically — it’s to build the instinct that says: “this should be automated, monitored, and survivable.” Once that instinct exists, the tooling is just syntax.

Start with a bot. End with a system.