Agent Media Tools

Deployment runbook

Deploy something you have already proved locally.

The generated manifests are reviewable starting points. Cloud interfaces, plans, and manifest schemas change. Preview cost and configuration before creating resources, keep secrets in the provider’s secret store, and verify the deployed process instead of assuming a green build means a working bot.

Choose the right process type

Bot platformCloud processPublic HTTP required?
Web chatWeb serviceYes; port supplied by the platform or 8000.
DiscordContinuous workerNo; it opens an outbound gateway connection.
TelegramContinuous workerNo for the generated long-polling adapter.
TerminalInteractive local processNo. Do not deploy it as an unattended worker.

Discord and Telegram need exactly one active polling/gateway process for a simple deployment. Multiple Telegram long-polling instances can compete for updates. Web chat can scale horizontally only if you accept per-instance SQLite memory or replace the local state design.

Pre-deployment checklist

  1. Complete the local setup runbook with the same provider, model, and platform.
  2. Run python bot.py --check and one real reply.
  3. Create a private Git repository if the cloud platform deploys from Git. Confirm .env, memory.db, logs, and private knowledge are absent.
  4. Choose a region near the model endpoint and intended users.
  5. Decide whether memory must survive deploys. Most platform filesystems are ephemeral unless a volume/disk is attached.
  6. Set provider budgets, rate limits, and alerts before making the service public.
  7. For web chat, generate a long random BOT_ACCESS_TOKEN and plan TLS plus upstream abuse controls.
# Inspect exactly what Git would receive
git status --short
git check-ignore .env memory.db

# Verify package before cloud work
python bot.py --check
python -m py_compile bot.py

Docker on one machine

This is the closest local rehearsal for every Docker-based cloud target.

cp .env.example .env
# Edit .env privately, then:
docker compose config
docker compose build --pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 bot

# Web export only
curl --fail http://127.0.0.1:8000/healthz

The Compose project mounts knowledge/ read-only and stores memory.db in the named bot-data volume through BOT_DATA_DIR=/data. Updating the image does not remove that volume.

# Safe application update; retains bot-data
docker compose build --pull
docker compose up -d

# Stop and retain memory
docker compose down

# DANGER: also deletes persisted bot memory
docker compose down -v
Do not publish port 8000 casually.

The generated token protects /chat, but a public service also needs HTTPS, firewall/security-group review, request and bandwidth limits, log review, and a plan for abuse. Keep /healthz public only if your platform requires it; it returns status, bot name, and template version.

Generic Linux VPS

  1. Create a non-root deployment user and install current Docker Engine plus the Compose plugin from your distribution’s trusted instructions.
  2. Copy or clone the project into a dedicated directory. Create .env on the server with restrictive permissions.
  3. Allow inbound SSH only from trusted networks. For web chat, expose 80/443 through a reverse proxy; do not expose the model server.
  4. Run Compose, verify health locally, then configure the proxy to forward HTTPS to 127.0.0.1:8000.
  5. Enable security updates, disk monitoring, log rotation, and provider spend alerts.
chmod 600 .env
docker compose up -d --build
curl --fail http://127.0.0.1:8000/healthz
docker compose logs --tail=100 bot

Back up the named volume only if you need conversation history. Treat backups as sensitive because they may contain user messages. Test restore to a disposable volume before depending on it.

Railway

Railway detects a root-level Dockerfile. Its current documentation explains that Compose concepts are translated into Railway services; the platform does not simply execute docker-compose.yml as a production stack.

  1. Push the extracted project to a private GitHub repository without .env.
  2. Create a Railway project and add a service from that repository. Confirm the build log says it detected the Dockerfile.
  3. In Variables, add the one provider key you need, LLM_BASE_URL if overridden, the platform token, optional AMT_AGENT_KEY, and BOT_ACCESS_TOKEN for web.
  4. For web, set HOST=0.0.0.0. The generated runtime reads Railway’s injected PORT; if you configure a target port manually, ensure the service and health check agree.
  5. Generate a public domain only for web chat. Workers need no public domain.
  6. If SQLite memory must persist, attach a Railway Volume at /data and set BOT_DATA_DIR=/data.
  7. Set the web health-check path to /healthz, deploy, then inspect build and deployment logs.
# Optional Railway CLI checks after linking the project
railway status
railway logs

See Railway’s official Dockerfile, variables, health-check, and Compose translation documentation.

Render

The included render.yaml defines a Docker web service for web chat or a Docker worker for Discord/Telegram. Render’s current Blueprint specification allows the free plan for web services but not background workers; the generated worker therefore uses Starter.

  1. Push the project to a private repository and choose New Blueprint in Render.
  2. Review the proposed service type, region, and plan before applying.
  3. Add credentials through the dashboard. Do not hardcode secrets into render.yaml. For a custom Blueprint, secret placeholders may use sync: false.
  4. For web, set HOST=0.0.0.0 and BOT_ACCESS_TOKEN. The manifest checks /healthz.
  5. If memory must persist, attach a paid persistent disk at /data and set BOT_DATA_DIR=/data. Render filesystems are otherwise ephemeral.
  6. Deploy, open the Events and Logs views, verify health, and send one private test message.

Render preserves existing environment variables omitted during later Blueprint syncs, but new sync: false variables on an existing service must be added manually. Consult the official Blueprint reference, environment-variable guide, and persistent-disk behavior.

Fly.io

  1. Install and authenticate flyctl, then review fly.toml. Change the globally unique app name and preferred region.
  2. Create the app configuration without deploying.
  3. Set secrets through Fly’s encrypted secrets interface. Setting secrets updates Machines, so plan this as a deployment change.
  4. If persistent memory is required, create a volume in the same region, add a mount at /data, and set BOT_DATA_DIR=/data before deployment.
  5. Deploy and verify status, logs, and health checks.
fly launch --copy-config --no-deploy
fly secrets set BOT_ACCESS_TOKEN=replace_privately
# Also set exactly the provider/platform keys required by this bot
fly secrets list
fly deploy
fly status
fly checks list
fly logs

Web exports bind Waitress to 0.0.0.0 on internal port 8000 in the generated configuration. Add an HTTP health check for /healthz if your reviewed fly.toml does not contain one. Read Fly’s official secrets, health checks, and HTTP service references.

DigitalOcean App Platform

  1. Replace YOUR_GITHUB_REPOSITORY in .do/app.yaml and connect the private repository.
  2. Use the App Platform spec preview or control panel to confirm the component is a service for web or worker for Discord/Telegram.
  3. Add provider and platform credentials as encrypted SECRET, runtime-scoped environment variables. Do not place their values in the spec.
  4. For web, keep HOST=0.0.0.0, port 8000, and add an HTTP health check for /healthz.
  5. Review instance size, region, alerts, deployment source, and estimated cost before creation.
  6. Deploy, inspect runtime logs, verify health, and send a controlled test message.

The generated spec is intentionally minimal. Validate it against DigitalOcean’s current App Spec reference and health-check guide. If durable SQLite memory is a requirement, confirm the platform’s current storage option before relying on local disk; otherwise choose a VPS with an explicit volume.

Private sharing with Tailscale

For a web bot intended only for your tailnet, keep the bot on loopback and use Tailscale Serve as the private HTTPS edge:

# Bot .env
HOST=127.0.0.1
PORT=8000
BOT_ACCESS_TOKEN=replace_with_a_long_random_value

python bot.py
# In another terminal, using current Tailscale CLI syntax:
tailscale serve 8000
tailscale serve status

Tailscale documents Serve for tailnet-only sharing. Funnel is public internet exposure and should not be treated as equivalent; review the Funnel security and availability model before using it.

Post-deployment verification

# Web service
curl --fail https://YOUR_DOMAIN/healthz

# Confirm chat rejects a missing token
curl -sS -o /dev/null -w '%{http_code}\n' \
  -H 'content-type: application/json' \
  --data '{"message":"health test"}' \
  https://YOUR_DOMAIN/chat
# Expected when BOT_ACCESS_TOKEN is configured: 401

Update and rollback

  1. Export or retain the previous config.json and deployment files.
  2. Back up memory.db only if retention is required; never copy it into Git.
  3. Import the old configuration into Bot Builder, review normalization, and download the new maintained template.
  4. Test locally and in a separate preview/staging service.
  5. Deploy the new image while preserving the same secret names and volume mount.
  6. If health or the private smoke test fails, redeploy the prior known-good image/commit. Restore memory only if the release changed or damaged it.
A code rollback does not undo conversation writes.

When SQLite memory is mounted persistently, rolling back the container leaves the current database in place. Keep the failed state for diagnosis and restore a backup only when necessary.