🚀 Containerizing My Go Telegram Bot: Lessons, Wins & Gotchas

Amurru ZeroukAmurru Zerouk
4 min read

Hey folks! 👋

Recently, I took on the task of containerizing my Telegram bot project — built with Go — and thought I’d share how that adventure went. The bot, which I lovingly call socdownbro, is a Telegram bot that fetches and downloads social media videos. It's based on Telegram Bot API Framework and uses yt-dlp, ffmpeg, and a few custom tweaks. While the Go codebase was solid, getting it into a clean, reusable Docker image took a bit of finesse.

So here’s a walk-through of my experience — including my Dockerfile, docker-compose.yaml, and a few nuggets of wisdom I picked up along the way.


🏗️ Step 1: Building the Dockerfile

Here’s the full Dockerfile I ended up with:

# Use a minimal image to run the application
FROM golang:1.24-alpine AS builder

# CGO_ENABLED is needed to build go-sqlite3
RUN apk add --no-cache --update go gcc g++

# Set working directory
WORKDIR /usr/src/app
ENV CGO_ENABLED=1

# Cache deps
COPY go.mod go.sum ./
RUN go mod download
RUN go mod verify

# Copy source
COPY . .

# Run tests (because CI/CD is in my blood 💉)
RUN go test -v ./...

# Build binary
RUN go build -v -o socdownbro .

# Final minimal image
FROM alpine:latest

# Add ffmpeg and yt-dlp for video processing
RUN apk --no-cache add ffmpeg yt-dlp

WORKDIR /bot
COPY --from=builder /usr/src/app/socdownbro .
COPY --from=builder /usr/src/app/config.json .
COPY res ./res/

CMD ["/bot/socdownbro"]

💡 Why Multi-Stage?

Multi-stage builds keep my image lean. I compile everything in the builder stage (with all the heavy Go toolchain), and then ship only what I need — the binary, config, and resources — into the final image. Clean and tiny. Alpine helps too!


🧩 Step 2: Orchestrating with Docker Compose

To keep things maintainable and reproducible, I used Docker Compose to wire up everything, including the PostgreSQL DB and Telegram Bot API server.

Here’s the docker-compose.yaml:

services:
  socialdownbro-bot:
    container_name: socialdownbro-bot
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - DB_DATABASE=${DB_NAME}
      - DB_USERNAME=${DB_USERNAME}
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_HOST=${DB_HOST}
      - DB_PORT=${DB_PORT}
      - DB_SCHEMA=${DB_SCHEMA}
    volumes:
      - videos:/bot/videos:rw
    depends_on:
      - botanium-db
      - telegram-bot-api
    restart: unless-stopped

  telegram-bot-api:
    image: aiogram/telegram-bot-api:latest
    environment:
      TELEGRAM_API_ID: "${TELEGRAM_API_ID}"
      TELEGRAM_API_HASH: "${TELEGRAM_API_HASH}"
    volumes:
      - telegram-bot-api-data:/var/lib/telegram-bot-api
    ports:
      - "8081:8081"
    restart: unless-stopped

  botanium-db:
    image: postgres:17-alpine
    environment:
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: ${DB_NAME}
    ports:
      - "127.0.0.1:5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  telegram-bot-api-data:
  pgdata:
  videos:

🔍 Highlights

  • depends_on: Ensures the DB and Telegram API start before the bot.

  • Volumes:

    • videos is used to persist downloads.

    • pgdata holds our database files.

    • telegram-bot-api-data keeps the Telegram API data stable between restarts.


🧠 What I Learned

✅ Wins

  • Go binaries are Docker’s best friend: A statically compiled Go app is incredibly easy to containerize. No JVMs, no interpreters. Just copy and run.

  • Multi-stage builds are game changers. Clean separation of concerns, faster rebuilds, smaller images.

  • Local testing was easy: Thanks to Compose, I could bring up the whole environment with a single command. Great for rapid iteration.

⚠️ Gotchas

  • SQLite in Docker: I had to enable CGO_ENABLED=1 and include build tools because of the go-sqlite3 dependency. Alpine doesn’t come with glibc, so this needed some tweaking.

  • yt-dlp & ffmpeg in Alpine: I initially tried to use Alpine’s versions, but sometimes they lag behind. If I hit any weird bugs, I might switch to a Debian-based image later on.

  • Telegram Bot API service: Running your own instance gives more control, but be ready to monitor it — especially for TLS and auth issues if you go public.


🤓 Final Thoughts

Containerizing my Telegram bot was super satisfying. Now I can deploy anywhere — my dev machine, a VPS, or even Kubernetes down the road. Plus, I get consistent, predictable environments. No more "it works on my machine" moments.

If you’re building bots with Go (or really anything else), I highly recommend giving Docker a shot. It’s not as scary as it looks, and once you get the hang of it, it’s like having a superpower. 🦸‍♂️

Let me know if you’d like me to open source socdownbro or write a follow-up on deploying it to the cloud. Till then — happy coding! 💻✨

2
Subscribe to my newsletter

Read articles from Amurru Zerouk directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Amurru Zerouk
Amurru Zerouk