Skip to content

Deployment Guide

This guide covers deployment, maintenance, and operations for the Signal Bot using the deploy.sh script.

  • Docker and Docker Compose v2+
  • SSH access to the production server
  • A registered Signal phone number for the bot

The deploy.sh script manages all deployment operations.

Terminal window
./deploy.sh [command] [options]
CommandDescription
deployFull deployment: sync code + rebuild + restart (default)
syncSync code, config, and memes without rebuilding
rebuildRebuild container without syncing
restartRestart container without rebuilding
logs [-t N]Show last N log lines (default: 50)
followFollow logs in real-time (Ctrl+C to stop)
statusShow container status and startup logs
shellOpen shell inside the container
envShow environment variables (masked)
test-apiTest PeerTube and Local AI connectivity
test-minioTest MinIO S3 configuration
test-shlinkTest Shlink URL shortener
test-formattingTest markdown-to-Signal formatting
sync-db-memesDownload all database memes to storage
cleanup [N]Remove old Docker images (keep last N, default: 5)
rebuild-signal-cliForce rebuild signal-cli from master (fixes binary ACI encoding)
helpShow usage information
OptionDescription
-v, --verboseShow verbose output
-n, --no-cacheForce rebuild without Docker cache
-t, --tail NNumber of log lines to show
Terminal window
# Standard deployment (sync + rebuild + restart)
./deploy.sh
# Deploy with fresh build (no cache)
./deploy.sh deploy --no-cache
# Only sync code without rebuilding
./deploy.sh sync
# View last 100 log lines
./deploy.sh logs -t 100
# Watch logs in real-time
./deploy.sh follow
# Check container status
./deploy.sh status
# Test integrations
./deploy.sh test-minio
./deploy.sh test-api
# Open shell for debugging
./deploy.sh shell
# Clean up old images (keep last 3)
./deploy.sh cleanup 3

When making code changes:

Terminal window
# 1. Make changes locally in src/src/
vim src/src/bot/command-handler.ts
# 2. Deploy with no-cache to ensure fresh build
./deploy.sh deploy --no-cache
# 3. Verify the deployment
./deploy.sh status
./deploy.sh logs -t 50

For environment variable or config changes:

Terminal window
# 1. Edit local config files
vim compose.yml
vim .env.example # Copy to server manually
# 2. Sync config files
./deploy.sh sync
# 3. Restart to apply
./deploy.sh restart

If the bot stops responding:

Terminal window
./deploy.sh restart
# or
./deploy.sh deploy

ItemValue
Hostproxmox (Tailscale IP: 100.87.20.88)
Remote Path/home/signal-bot-selfhosted/
Container Namesignal-bot-selfhosted-signal-bot-1
Compose Servicesignal-bot
/home/signal-bot-selfhosted/
├── .env # Environment variables (credentials)
├── compose.yml # Docker Compose configuration
├── Dockerfile.selfhosted # Docker build file
├── src/ # Bot source code (synced)
│ ├── src/ # TypeScript source
│ ├── dist/ # Compiled JavaScript
│ └── package.json
├── signal-data/ # Signal CLI account data (CRITICAL!)
├── data/
│ ├── postgres/ # PostgreSQL data volume
│ ├── redis/ # Redis data volume
│ ├── memes/ # Persistent meme storage
│ └── backups/ # Database backups
├── database/ # SQL migration scripts
│ ├── 000_base_schema.sql
│ └── 001_*.sql ...
└── memes/ # Local meme GIF files

The deploy script syncs:

SourceDestinationNotes
./src//home/signal-bot-selfhosted/src/Source code (excludes node_modules, .git)
./compose.yml/home/signal-bot-selfhosted/compose.ymlCreates backup first
./Dockerfile.selfhosted/home/signal-bot-selfhosted/Dockerfile.selfhostedBuild configuration
./memes//home/signal-bot-selfhosted/data/memes/Local meme files

NOT synced (preserved on server):

  • .env - Contains credentials
  • signal-data/ - Signal CLI registration
  • data/postgres/ - Database files
  • data/redis/ - Cache data

The bot uses multiple Docker networks:

NetworkPurpose
signal-bot-networkInternal bot communication
minio-networkExternal: MinIO S3 access
postgres-networkExternal: Shlink access

Important: Use full container names (e.g., signal-bot-postgres) instead of short names to avoid DNS conflicts across networks.


Terminal window
curl http://localhost:8080/health

Returns:

{
"status": "ok",
"timestamp": "2026-01-23T12:00:00.000Z",
"uptime": 86400,
"memory": { "rss": 150000000, "heapUsed": 80000000 }
}

The bot includes automatic monitoring:

  • Daemon health check: Every 60 seconds
  • Auto-restart: On daemon death or disconnect
  • Message flow monitoring: Alerts if no messages for 10+ minutes
  • Restart notifications: Sends alerts to Bot Dev room
Terminal window
# Check container status
./deploy.sh status
# View health endpoint
ssh proxmox "docker exec signal-bot-selfhosted-signal-bot-1 curl -s http://localhost:8080/health | jq"
# Check for errors in logs
./deploy.sh logs -t 200 | grep -i error

The signal-data/ directory contains Signal CLI registration data. If lost, you must re-register the phone number.

Terminal window
# Backup signal-data
ssh proxmox "cd /home/signal-bot-selfhosted && tar -czf signal-data-backup-$(date +%Y%m%d).tar.gz signal-data/"
# Download to local machine
scp proxmox:/home/signal-bot-selfhosted/signal-data-backup-*.tar.gz ./backups/
Terminal window
# Create backup on server
ssh proxmox "docker exec signal-bot-postgres pg_dump -U signal_bot signal_bot > /home/signal-bot-selfhosted/data/backups/backup_$(date +%Y%m%d).sql"
# Compressed backup
ssh proxmox "docker exec signal-bot-postgres pg_dump -U signal_bot signal_bot | gzip > /home/signal-bot-selfhosted/data/backups/backup_$(date +%Y%m%d).sql.gz"

Create a cron job on the server:

Terminal window
# Edit crontab
crontab -e
# Add daily backup at 3 AM
0 3 * * * docker exec signal-bot-postgres pg_dump -U signal_bot signal_bot | gzip > /home/signal-bot-selfhosted/data/backups/backup_$(date +\%Y\%m\%d).sql.gz

Terminal window
# Stop the bot
docker compose stop signal-bot
# Restore database
docker exec -i signal-bot-postgres psql -U signal_bot signal_bot < backup.sql
# Start the bot
docker compose start signal-bot
Terminal window
# Stop the bot
docker compose stop signal-bot
# Extract backup
cd /home/signal-bot-selfhosted
tar -xzf signal-data-backup-20260123.tar.gz
# Start the bot
docker compose start signal-bot

If the entire server is lost:

  1. Set up new server with Docker
  2. Clone repository and copy config
  3. Restore signal-data/ backup (or re-register)
  4. Restore database backup
  5. Run ./deploy.sh deploy

Terminal window
ssh proxmox "docker system df"
Terminal window
# Keep last 5 images (default)
./deploy.sh cleanup
# Keep last 3 images
./deploy.sh cleanup 3

This removes:

  • Old signal-bot images
  • Dangling images
  • Build cache (keeps 2GB)

Terminal window
# Check logs for errors
./deploy.sh logs -t 100
# Common issues:
# - Missing environment variables
# - Database connection failed
# - Signal-CLI registration missing
Terminal window
# Force rebuild with no cache
./deploy.sh deploy --no-cache
Terminal window
# Check what's in the container
./deploy.sh env
# Verify specific variables
ssh proxmox "docker exec signal-bot-selfhosted-signal-bot-1 printenv | grep ADMIN"

  • Never commit .env files to git
  • Use unique, strong passwords for database
  • Rotate API tokens periodically
  • Restrict admin UUIDs to trusted users
  • Container runs as node user (not root)
  • Network isolation via Docker networks
  • Only necessary ports exposed
  • Keep signal-data/ backed up securely
  • Don’t share Signal registration data
  • Use a dedicated phone number for the bot