Skip to content

Deploying ​

A Ruby bot is a normal Python process. Run it however you like, as long as it starts in the bot's folder, with ruby or python -m ruby.

Containers ​

A two-stage build with uv keeps the image small. This is what one bot uses in the Containerfile:

dockerfile
# ---- Build stage ----
FROM ghcr.io/astral-sh/uv:python3.14-trixie-slim AS builder

# git, so uv can fetch Ruby from Codeberg
RUN apt-get update \
    && apt-get install -y --no-install-recommends git ca-certificates \
    && rm -rf /var/lib/apt/lists/*

ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy UV_NO_DEV=1 UV_PYTHON_DOWNLOADS=0
WORKDIR /app

# Dependencies first, so code changes don't reinstall everything
COPY pyproject.toml uv.lock ./
RUN uv sync --locked --no-install-project

COPY . .
RUN uv sync --locked --no-editable

# ---- Runtime stage ----
FROM python:3.14-slim-trixie
ENV PYTHONUNBUFFERED=1 PATH="/app/.venv/bin:$PATH"
WORKDIR /app

RUN groupadd --system app && useradd --system --gid app --home-dir /app app
COPY --from=builder --chown=app:app /app /app
USER app

CMD ["ruby"]

And a docker-compose.yml file with MongoDB:

yaml
services:
  mongo:
    image: docker.io/library/mongo:8
    restart: unless-stopped
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_USERNAME:?Set MONGO_USERNAME in .env}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD:?Set MONGO_PASSWORD in .env}
    volumes:
      - mongo_data:/data/db
    healthcheck:
      test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping').ok"]
      interval: 10s
      retries: 5

  bot:
    build: .
    restart: on-failure
    depends_on:
      mongo:
        condition: service_healthy
    env_file: .env  # TOKEN, MONGO_URI=mongodb://user:password@mongo:27017

volumes:
  mongo_data:

config.yaml is baked into the image

With the Containerfile above, config.yaml and cogs/ are copied in at build time, so changing the config means rebuilding. Recreate the container afterwards too: with podman-compose, up -d keeps the old container unless you add --force-recreate.

sh
podman-compose build bot && podman-compose up -d --force-recreate --no-deps bot

To skip rebuilding for config changes, mount it instead: volumes: ["./config.yaml:/app/config.yaml:ro"].

Updating Ruby ​

uv pins Ruby to a commit in uv.lock. To pick up a new version:

sh
uv lock --upgrade-package ruby

Then rebuild.

systemd (no container) ​

ini
[Unit]
Description=Ruby Bot
After=network-online.target

[Service]
WorkingDirectory=/opt/ruby-bot
ExecStart=/opt/ruby-bot/.venv/bin/python -m ruby
Restart=on-failure
User=rubybot

[Install]
WantedBy=multi-user.target