atBB Deployment Guide#
Version: 1.2 Last Updated: 2026-02-26 Audience: System administrators deploying atBB to production
Related Documentation:
- docs/trust-model.md — Trust model for self-hosted deployment: what the AppView controls, user data guarantees, and security implications
- docs/plans/complete/2026-02-11-deployment-infrastructure-design.md — Architectural decisions and design rationale behind this deployment approach
Table of Contents#
- Prerequisites
- Quick Start
- Environment Configuration
- Database Setup
- Running the Container
- Reverse Proxy Setup
- Monitoring & Logs
- Upgrading
- Troubleshooting
- Docker Compose Example
- NixOS Deployment
1. Prerequisites#
Before deploying atBB, ensure you have the following:
Infrastructure Requirements#
-
PostgreSQL 14+
- Managed service recommended: AWS RDS, DigitalOcean Managed Database, Azure Database for PostgreSQL, or similar
- Minimum 1GB RAM, 10GB storage (scales with forum size)
- SSL/TLS support enabled (
?sslmode=require) - Database user with CREATE/ALTER/SELECT/INSERT/UPDATE/DELETE permissions
-
Domain Name & DNS
- Registered domain name (e.g.,
forum.example.com) - DNS A/AAAA record pointing to your server's public IP
- Recommended: wildcard DNS for future subdomains (
*.forum.example.com)
- Registered domain name (e.g.,
-
Container Runtime
- Docker 20.10+ or Docker Desktop
- Minimum 512MB RAM allocated to container (1GB+ recommended)
- 2GB disk space for container image and logs
AT Protocol Requirements#
IMPORTANT: atBB integrates with the AT Protocol network (the decentralized protocol powering Bluesky). You must set up your forum's AT Protocol identity before deployment.
1. Choose a Personal Data Server (PDS)#
Your forum needs a PDS to store its records (forum metadata, categories, moderation actions). Options:
-
Self-hosted PDS: Run your own PDS instance (advanced, recommended for sovereignty)
- Guide: https://github.com/bluesky-social/pds
- Requires separate server and domain
- Full control over data and federation
-
Hosted PDS: Use Bluesky's PDS (
https://bsky.social) or another provider- Simpler setup, lower maintenance
- Suitable for testing and small forums
2. Create Forum Account#
Create an account for your forum on your chosen PDS:
# Example with Bluesky PDS
# Visit https://bsky.app and create account with your forum's handle
# Handle should match your domain: forum.example.com
Record these values (you'll need them later):
- Forum Handle:
forum.example.com - Forum Password: (choose a strong password, minimum 16 characters)
- Forum DID:
did:plc:xxxxxxxxxxxxx(found in account settings or PDS admin interface) - PDS URL:
https://bsky.social(or your PDS URL)
3. Understand Lexicon Namespace#
atBB uses the space.atbb.* lexicon namespace for its records:
space.atbb.forum.forum— Forum metadata (name, description, rules)space.atbb.forum.category— Forum categoriesspace.atbb.post— User posts and repliesspace.atbb.membership— User membership recordsspace.atbb.modAction— Moderation actions
Your forum's DID will own the forum-level records, while users' DIDs own their posts and memberships.
Security Requirements#
- TLS/SSL Certificate: Let's Encrypt (free) or commercial certificate
- Firewall: Restrict inbound ports to 80/443 only
- SSH Access: Key-based authentication (disable password auth)
- Secrets Management: Secure storage for environment variables (consider cloud secrets manager)
Before deploying: Read docs/trust-model.md. It explains what the AppView controls (the Forum DID's credentials and write access), what your users can count on, and the security implications of a compromised server.
2. Quick Start#
Follow these steps for a minimal working deployment. Detailed explanations follow in later sections.
Step 1: Pull the Docker Image#
# Pull latest stable version
docker pull ghcr.io/malpercio-dev/atbb:latest
# Or pin to a specific version (recommended for production)
docker pull ghcr.io/malpercio-dev/atbb:v1.0.0
Expected output:
latest: Pulling from malpercio-dev/atbb
e7c96db7181b: Pull complete
...
Status: Downloaded newer image for ghcr.io/malpercio-dev/atbb:latest
Step 2: Create Environment File#
# Copy the template
curl -o .env.production https://raw.githubusercontent.com/malpercio-dev/atbb-monorepo/main/.env.production.example
# Generate a strong session secret
openssl rand -hex 32
# Output: a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456
Edit .env.production and fill in these REQUIRED values:
# Database connection (from your PostgreSQL provider)
DATABASE_URL=postgresql://atbb_user:YOUR_DB_PASSWORD@db.example.com:5432/atbb_prod?sslmode=require
# AT Protocol credentials (from Prerequisites step)
FORUM_DID=did:plc:YOUR_FORUM_DID
PDS_URL=https://bsky.social
FORUM_HANDLE=forum.example.com
FORUM_PASSWORD=YOUR_FORUM_PASSWORD
# OAuth configuration (your public domain)
OAUTH_PUBLIC_URL=https://forum.example.com
# Session security (use the openssl output from above)
SESSION_SECRET=a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456
Secure the file:
chmod 600 .env.production
Step 3: Run Database Migrations#
CRITICAL: Run migrations BEFORE starting the application. This creates the database schema.
docker run --rm \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:latest \
pnpm --filter @atbb/appview db:migrate
Expected output:
> @atbb/db@0.1.0 db:migrate
> drizzle-kit migrate
Reading migrations from migrations/
Applying migration: 0000_initial_schema.sql
Migration applied successfully
If this fails, DO NOT proceed. See Section 4: Database Setup for troubleshooting.
Step 4: Start the Container#
docker run -d \
--name atbb \
--restart unless-stopped \
-p 8080:80 \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:latest
Options explained:
-d— Run in background (detached mode)--name atbb— Name the container for easy management--restart unless-stopped— Auto-restart on crashes or server reboot-p 8080:80— Map host port 8080 to container port 80--env-file .env.production— Load environment variables
Verify the container is running:
docker ps | grep atbb
# Expected: Container with STATUS "Up X seconds"
docker logs atbb
# Expected: No errors, services starting
Test the application:
curl http://localhost:8080/api/healthz
# Expected: {"status":"ok"}
Step 5: Configure Reverse Proxy#
The container is now running on port 8080, but NOT accessible publicly yet. You need a reverse proxy to:
- Terminate TLS/SSL (HTTPS)
- Forward traffic from your domain to the container
- Handle automatic certificate renewal
Recommended setup with Caddy (automatic HTTPS):
Install Caddy:
# Ubuntu/Debian
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
Edit /etc/caddy/Caddyfile:
forum.example.com {
reverse_proxy localhost:8080
}
Reload Caddy:
sudo systemctl reload caddy
Caddy will automatically obtain a Let's Encrypt certificate and enable HTTPS.
Step 6: Verify Deployment#
Visit your forum: https://forum.example.com
Expected: atBB home page loads with no errors.
If you see errors, proceed to Section 9: Troubleshooting.
3. Environment Configuration#
Complete reference for all environment variables. See .env.production.example for detailed comments.
Required Variables#
| Variable | Description | Example |
|---|---|---|
DATABASE_URL |
Database connection string (PostgreSQL or SQLite) | PostgreSQL: postgresql://user:pass@host:5432/dbname?sslmode=require; SQLite: file:./atbb.db |
FORUM_DID |
Forum's AT Protocol DID | did:plc:abcdef1234567890 |
PDS_URL |
Personal Data Server URL | https://bsky.social |
FORUM_HANDLE |
Forum's AT Protocol handle | forum.example.com |
FORUM_PASSWORD |
Forum account password | (minimum 16 characters, alphanumeric + symbols) |
OAUTH_PUBLIC_URL |
Public URL for OAuth redirects | https://forum.example.com (MUST be HTTPS in production) |
SESSION_SECRET |
Session encryption key | Generate with: openssl rand -hex 32 |
Optional Variables#
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
AppView API port (internal) |
WEB_PORT |
3001 |
Web UI port (internal) |
APPVIEW_URL |
http://localhost:3000 |
Internal API URL (keep as localhost for single container) |
JETSTREAM_URL |
wss://jetstream2.us-east.bsky.network/subscribe |
AT Protocol firehose URL |
SESSION_TTL_DAYS |
30 |
Session lifetime in days (1-90 range) |
REDIS_URL |
(none) | Redis connection string (future: multi-instance deployments) |
Security Best Practices#
SESSION_SECRET Generation:
# CRITICAL: Never use a predictable value or leave blank
openssl rand -hex 32
# Use different secrets for dev/staging/production
# Rotating the secret invalidates all active sessions
Password Requirements:
- Minimum 16 characters
- Mix of uppercase, lowercase, numbers, symbols
- Unique per environment (never reuse)
- Store in password manager or secrets vault
Connection String Security:
# Good: SSL/TLS enforced
DATABASE_URL=postgresql://user:pass@host:5432/db?sslmode=require
# Bad: Plain text connection (vulnerable to MITM)
DATABASE_URL=postgresql://user:pass@host:5432/db
File Permissions:
# Protect your environment file
chmod 600 .env.production
# Verify permissions
ls -la .env.production
# Expected: -rw------- (read/write for owner only)
Environment Loading Methods#
Docker CLI:
# Recommended: Load from file with --init for better signal handling
docker run --init --env-file .env.production ghcr.io/malpercio-dev/atbb:latest
# Alternative: Individual variables (for orchestrators)
docker run --init \
-e DATABASE_URL="postgresql://..." \
-e FORUM_DID="did:plc:..." \
-e SESSION_SECRET="..." \
ghcr.io/malpercio-dev/atbb:latest
Note: The --init flag enables tini as PID 1, improving signal handling for graceful shutdown. While not strictly required (the container has its own signal handling), it's considered best practice.
Docker Compose:
services:
atbb:
image: ghcr.io/malpercio-dev/atbb:latest
env_file:
- .env.production
Kubernetes:
# Use Secrets (NOT ConfigMaps for sensitive data)
apiVersion: v1
kind: Secret
metadata:
name: atbb-secrets
type: Opaque
stringData:
DATABASE_URL: "postgresql://..."
SESSION_SECRET: "..."
---
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: atbb
envFrom:
- secretRef:
name: atbb-secrets
4. Database Setup#
atBB supports two database backends:
- PostgreSQL (recommended for production) — full-featured, suitable for multi-user/multi-server deployments
- SQLite/LibSQL (lightweight alternative) — single-file database, ideal for small self-hosted forums. Use a
file:prefix inDATABASE_URL(e.g.file:./atbb.db) and run the SQLite-specific migrations (docker-compose.sqlite.ymlfor Docker or setdatabase.type = "sqlite"in the NixOS module).
The rest of this section covers PostgreSQL provisioning. SQLite requires no separate server setup — just point DATABASE_URL at a file path.
PostgreSQL Provisioning#
Option 1: Managed Database (Recommended)#
AWS RDS:
- Navigate to RDS Console → Create Database
- Choose PostgreSQL 14+ (latest stable version)
- Select appropriate instance size:
- Small forum (<1000 users):
db.t3.microordb.t4g.micro - Medium forum (1000-10000 users):
db.t3.smallordb.t4g.small - Large forum (10000+ users):
db.t3.mediumor higher
- Small forum (<1000 users):
- Enable "Storage Auto Scaling" (start with 20GB)
- Enable "Automated Backups" (7-30 day retention)
- Enable "Publicly Accessible" only if container is in different VPC
- Security group: Allow PostgreSQL (5432) from container's IP/VPC
- Create database:
atbb_prod - Create user:
atbb_userwith generated password
Connection string format:
postgresql://atbb_user:PASSWORD@instance-name.region.rds.amazonaws.com:5432/atbb_prod?sslmode=require
DigitalOcean Managed Database:
- Navigate to Databases → Create → PostgreSQL
- Choose datacenter closest to your Droplet/container
- Select plan (Basic $15/mo sufficient for small forums)
- Create database:
atbb_prod - Create user:
atbb_userwith generated password - Add trusted source: Your Droplet's IP or "All" for simplicity
- Download CA certificate (optional, for certificate validation)
Connection string provided in dashboard (copy and use directly).
Azure Database for PostgreSQL:
- Navigate to Azure Database for PostgreSQL → Create
- Choose "Flexible Server" (simpler, cheaper)
- Select region and compute tier (Burstable B1ms sufficient for small forums)
- Enable "High Availability" for production (optional)
- Configure firewall: Add your container's public IP
- Create database:
atbb_prod
Connection string format:
postgresql://atbb_user@servername:PASSWORD@servername.postgres.database.azure.com:5432/atbb_prod?sslmode=require
Option 2: Self-Hosted PostgreSQL#
Installation (Ubuntu/Debian):
# Install PostgreSQL
sudo apt update
sudo apt install -y postgresql postgresql-contrib
# Start and enable service
sudo systemctl enable postgresql
sudo systemctl start postgresql
Create database and user:
sudo -u postgres psql
-- In psql prompt:
CREATE DATABASE atbb_prod;
CREATE USER atbb_user WITH PASSWORD 'YOUR_STRONG_PASSWORD';
GRANT ALL PRIVILEGES ON DATABASE atbb_prod TO atbb_user;
\q
Enable remote connections (if container is on different host):
Edit /etc/postgresql/14/main/postgresql.conf:
listen_addresses = '*' # Or specific IP
Edit /etc/postgresql/14/main/pg_hba.conf:
# Add this line (replace 0.0.0.0/0 with specific IP range in production)
host atbb_prod atbb_user 0.0.0.0/0 scram-sha-256
Restart PostgreSQL:
sudo systemctl restart postgresql
Connection string:
postgresql://atbb_user:YOUR_STRONG_PASSWORD@your-server-ip:5432/atbb_prod
Running Database Migrations#
Migrations create the database schema (tables, indexes, constraints).
First-time setup:
docker run --rm \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:latest \
pnpm --filter @atbb/appview db:migrate
Options explained:
--rm— Remove container after migration completes--env-file .env.production— Load database connection stringpnpm --filter @atbb/appview db:migrate— Run Drizzle migrations
Expected output (success):
Reading migrations from /app/packages/db/migrations
Applying migration: 0000_initial_schema.sql
Applying migration: 0001_add_deleted_flag.sql
All migrations applied successfully
Verify migrations:
# Connect to your database
psql "postgresql://atbb_user:PASSWORD@host:5432/atbb_prod?sslmode=require"
# List tables
\dt
# Expected output (12 tables):
# Schema | Name | Type | Owner
# --------+-----------------------+-------+-----------
# public | backfill_errors | table | atbb_user
# public | backfill_progress | table | atbb_user
# public | boards | table | atbb_user
# public | categories | table | atbb_user
# public | firehose_cursor | table | atbb_user
# public | forums | table | atbb_user
# public | memberships | table | atbb_user
# public | mod_actions | table | atbb_user
# public | posts | table | atbb_user
# public | role_permissions | table | atbb_user
# public | roles | table | atbb_user
# public | users | table | atbb_user
Migration Troubleshooting#
Error: "database does not exist"
FATAL: database "atbb_prod" does not exist
Solution: Create the database first (see self-hosted instructions above, or create via cloud console).
Error: "password authentication failed"
FATAL: password authentication failed for user "atbb_user"
Solution: Verify credentials in DATABASE_URL match database user.
Error: "connection refused"
Error: connect ECONNREFUSED
Solution:
- Check database host/port are correct
- Verify firewall allows connections from container's IP
- For cloud databases, ensure "trusted sources" includes your IP
Error: "SSL connection required"
FATAL: no pg_hba.conf entry for host, SSL off
Solution: Add ?sslmode=require to connection string.
Error: "permission denied for schema public"
ERROR: permission denied for schema public
Solution: Grant schema permissions:
GRANT USAGE ON SCHEMA public TO atbb_user;
GRANT CREATE ON SCHEMA public TO atbb_user;
5. Running the Container#
Basic Deployment#
Production command (recommended):
docker run -d \
--name atbb \
--restart unless-stopped \
-p 8080:80 \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:latest
Pin to specific version (recommended for stability):
docker run -d \
--name atbb \
--restart unless-stopped \
-p 8080:80 \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:v1.0.0
Pin to specific commit SHA (for rollback/testing):
docker run -d \
--name atbb \
--restart unless-stopped \
-p 8080:80 \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:main-a1b2c3d
Advanced Options#
Custom port mapping:
# Expose on different host port
docker run -d \
--name atbb \
-p 3000:80 \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:latest
# Bind to specific interface (localhost only)
docker run -d \
--name atbb \
-p 127.0.0.1:8080:80 \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:latest
Resource limits:
docker run -d \
--name atbb \
--restart unless-stopped \
-p 8080:80 \
--memory="1g" \
--cpus="1.0" \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:latest
Custom network:
# Create network
docker network create atbb-network
# Run with network
docker run -d \
--name atbb \
--network atbb-network \
-p 8080:80 \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:latest
Container Management#
View logs:
# All logs
docker logs atbb
# Follow logs (live)
docker logs -f atbb
# Last 100 lines
docker logs --tail 100 atbb
# Logs since timestamp
docker logs --since 2026-02-12T10:00:00 atbb
Stop container:
docker stop atbb
Start stopped container:
docker start atbb
Restart container:
docker restart atbb
Remove container:
# Stop first
docker stop atbb
# Remove
docker rm atbb
Execute commands inside container (debugging):
# Interactive shell
docker exec -it atbb sh
# Run single command
docker exec atbb ps aux
docker exec atbb df -h
docker exec atbb cat /etc/nginx/nginx.conf
Health Checks#
The container exposes a health endpoint:
Check via curl:
curl http://localhost:8080/api/healthz
Expected response:
{"status":"ok"}
Check via Docker:
docker inspect atbb | grep -A 5 Health
Use in monitoring scripts:
#!/bin/bash
# health-check.sh
HEALTH=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/api/healthz)
if [ "$HEALTH" != "200" ]; then
echo "ALERT: atBB health check failed (HTTP $HEALTH)"
# Send alert (email, Slack, PagerDuty, etc.)
exit 1
fi
echo "OK: atBB is healthy"
exit 0
Run as cron job:
# Check every 5 minutes
*/5 * * * * /path/to/health-check.sh >> /var/log/atbb-health.log 2>&1
6. Reverse Proxy Setup#
The container exposes HTTP on port 80. In production, you need a reverse proxy to:
- Terminate TLS/SSL (enable HTTPS)
- Manage domain routing
- Handle certificate renewal
- Provide additional security headers
Caddy (Recommended)#
Why Caddy:
- Automatic HTTPS with Let's Encrypt (zero configuration)
- Simple configuration syntax
- Auto-renewal of certificates
- Modern defaults (HTTP/2, security headers)
Installation:
Ubuntu/Debian:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
CentOS/RHEL:
dnf install 'dnf-command(copr)'
dnf copr enable @caddy/caddy
dnf install caddy
Basic Configuration:
Edit /etc/caddy/Caddyfile:
forum.example.com {
reverse_proxy localhost:8080
}
Advanced Configuration (with security headers):
forum.example.com {
# Reverse proxy to atBB container
reverse_proxy localhost:8080
# Security headers
header {
# Enable HSTS (force HTTPS)
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Prevent clickjacking
X-Frame-Options "SAMEORIGIN"
# Prevent MIME sniffing
X-Content-Type-Options "nosniff"
# XSS protection
X-XSS-Protection "1; mode=block"
# Referrer policy
Referrer-Policy "strict-origin-when-cross-origin"
# Content Security Policy (adjust as needed)
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"
}
# Access logs
log {
output file /var/log/caddy/atbb-access.log
format json
}
}
Apply configuration:
# Validate configuration
sudo caddy validate --config /etc/caddy/Caddyfile
# Reload Caddy (no downtime)
sudo systemctl reload caddy
# Check status
sudo systemctl status caddy
Verify HTTPS:
curl -I https://forum.example.com
# Expected: HTTP/2 200 with security headers
nginx#
Installation:
sudo apt install -y nginx
Configuration:
Create /etc/nginx/sites-available/atbb:
# HTTP -> HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name forum.example.com;
return 301 https://$server_name$request_uri;
}
# HTTPS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name forum.example.com;
# SSL certificates (obtain via certbot)
ssl_certificate /etc/letsencrypt/live/forum.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/forum.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/forum.example.com/chain.pem;
# SSL settings (Mozilla Modern configuration)
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Proxy to atBB container
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support (for future features)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Access logs
access_log /var/log/nginx/atbb-access.log combined;
error_log /var/log/nginx/atbb-error.log;
}
Obtain SSL certificate with Certbot:
# Install Certbot
sudo apt install -y certbot python3-certbot-nginx
# Obtain certificate (interactive)
sudo certbot --nginx -d forum.example.com
# Certbot will automatically:
# - Validate domain ownership
# - Obtain certificate from Let's Encrypt
# - Update nginx configuration
# - Set up auto-renewal
Enable site:
sudo ln -s /etc/nginx/sites-available/atbb /etc/nginx/sites-enabled/
sudo nginx -t # Test configuration
sudo systemctl reload nginx
Traefik#
docker-compose.yml with Traefik:
version: '3.8'
services:
traefik:
image: traefik:v2.11
command:
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./letsencrypt:/letsencrypt"
atbb:
image: ghcr.io/malpercio-dev/atbb:latest
env_file:
- .env.production
labels:
- "traefik.enable=true"
- "traefik.http.routers.atbb.rule=Host(`forum.example.com`)"
- "traefik.http.routers.atbb.entrypoints=websecure"
- "traefik.http.routers.atbb.tls.certresolver=letsencrypt"
- "traefik.http.services.atbb.loadbalancer.server.port=80"
Start with:
docker-compose up -d
7. Monitoring & Logs#
Container Logs#
View logs:
# All logs
docker logs atbb
# Follow logs (real-time)
docker logs -f atbb
# Filter by timestamp
docker logs --since 2026-02-12T10:00:00 atbb
docker logs --until 2026-02-12T12:00:00 atbb
Log format: JSON structured logs
Example log entry:
{
"level": "info",
"time": "2026-02-12T14:30:00.000Z",
"service": "appview",
"msg": "HTTP request",
"method": "GET",
"path": "/api/forum",
"status": 200,
"duration": 15
}
Parse logs with jq:
# Filter by level
docker logs atbb | grep '^{' | jq 'select(.level == "error")'
# Extract errors from last hour
docker logs --since 1h atbb | grep '^{' | jq 'select(.level == "error")'
# Count requests by path
docker logs atbb | grep '^{' | jq -r '.path' | sort | uniq -c | sort -nr
Log Persistence#
Forward to log aggregator:
Using Docker logging driver (syslog):
docker run -d \
--name atbb \
--log-driver syslog \
--log-opt syslog-address=udp://logserver:514 \
--log-opt tag="atbb" \
-p 8080:80 \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:latest
Using Docker logging driver (json-file with rotation):
docker run -d \
--name atbb \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
-p 8080:80 \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:latest
Health Monitoring#
Health endpoint: GET /api/healthz
Example monitoring script (save as /usr/local/bin/atbb-health-check):
#!/bin/bash
# atbb-health-check - Monitor atBB health and restart if needed
CONTAINER_NAME="atbb"
HEALTH_URL="http://localhost:8080/api/healthz"
MAX_FAILURES=3
FAILURES=0
while true; do
# Check health endpoint
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_URL")
if [ "$HTTP_CODE" != "200" ]; then
FAILURES=$((FAILURES + 1))
echo "$(date): Health check failed (HTTP $HTTP_CODE), failures: $FAILURES/$MAX_FAILURES"
if [ "$FAILURES" -ge "$MAX_FAILURES" ]; then
echo "$(date): Max failures reached, restarting container"
docker restart "$CONTAINER_NAME"
FAILURES=0
sleep 60 # Wait for restart
fi
else
# Reset failure counter on success
if [ "$FAILURES" -gt 0 ]; then
echo "$(date): Health check recovered"
fi
FAILURES=0
fi
sleep 60 # Check every minute
done
Run as systemd service:
sudo chmod +x /usr/local/bin/atbb-health-check
cat <<EOF | sudo tee /etc/systemd/system/atbb-health-check.service
[Unit]
Description=atBB Health Check Monitor
After=docker.service
Requires=docker.service
[Service]
Type=simple
ExecStart=/usr/local/bin/atbb-health-check
Restart=always
StandardOutput=append:/var/log/atbb-health-check.log
StandardError=append:/var/log/atbb-health-check.log
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable atbb-health-check
sudo systemctl start atbb-health-check
Resource Monitoring#
Monitor container resource usage:
# Real-time stats
docker stats atbb
# Example output:
# CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
# atbb 2.5% 256MiB / 1GiB 25% 1.2MB/5MB 0B/0B
Set up alerts for resource limits:
#!/bin/bash
# atbb-resource-alert - Alert on high resource usage
CONTAINER="atbb"
CPU_THRESHOLD=80
MEM_THRESHOLD=80
STATS=$(docker stats --no-stream --format "{{.CPUPerc}},{{.MemPerc}}" "$CONTAINER")
CPU=$(echo "$STATS" | cut -d',' -f1 | tr -d '%')
MEM=$(echo "$STATS" | cut -d',' -f2 | tr -d '%')
if [ "$(echo "$CPU > $CPU_THRESHOLD" | bc)" -eq 1 ]; then
echo "ALERT: CPU usage is ${CPU}% (threshold: ${CPU_THRESHOLD}%)"
# Send notification (email, Slack, etc.)
fi
if [ "$(echo "$MEM > $MEM_THRESHOLD" | bc)" -eq 1 ]; then
echo "ALERT: Memory usage is ${MEM}% (threshold: ${MEM_THRESHOLD}%)"
# Send notification
fi
Future: Observability#
Planned enhancements (not yet implemented):
- Prometheus metrics endpoint (
/api/metrics) - OpenTelemetry tracing
- Grafana dashboard templates
- Alert manager integration
8. Upgrading#
Upgrade Process#
IMPORTANT: Upgrading will cause brief downtime (sessions are stored in memory and will be lost).
Step 1: Check release notes
# View releases on GitHub
# https://github.com/malpercio-dev/atbb-monorepo/releases
# Look for:
# - Breaking changes
# - Database migration requirements
# - New environment variables
Step 2: Backup database
# Backup current database (critical!)
pg_dump "postgresql://atbb_user:PASSWORD@host:5432/atbb_prod?sslmode=require" \
> atbb_backup_$(date +%Y%m%d_%H%M%S).sql
# Verify backup
ls -lh atbb_backup_*.sql
Step 3: Pull new image
# Pull specific version
docker pull ghcr.io/malpercio-dev/atbb:v1.1.0
# Or pull latest
docker pull ghcr.io/malpercio-dev/atbb:latest
Step 4: Run migrations (if required)
# Check release notes for migration requirements
# If migrations are needed:
docker run --rm \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:v1.1.0 \
pnpm --filter @atbb/appview db:migrate
Step 5: Stop old container
docker stop atbb
docker rm atbb
Step 6: Start new container
docker run -d \
--name atbb \
--restart unless-stopped \
-p 8080:80 \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:v1.1.0
Step 7: Verify upgrade
# Check logs for errors
docker logs atbb
# Test health endpoint
curl http://localhost:8080/api/healthz
# Visit forum in browser
# Test key functionality (login, post, etc.)
Rollback Procedure#
If upgrade fails, rollback to previous version:
Step 1: Stop broken container
docker stop atbb
docker rm atbb
Step 2: Restore database (if migrations were run)
# Connect to database
psql "postgresql://atbb_user:PASSWORD@host:5432/atbb_prod?sslmode=require"
# Drop all tables
DROP SCHEMA public CASCADE;
CREATE SCHEMA public;
# Restore from backup
psql "postgresql://atbb_user:PASSWORD@host:5432/atbb_prod?sslmode=require" \
< atbb_backup_20260212_140000.sql
Step 3: Start old version
docker run -d \
--name atbb \
--restart unless-stopped \
-p 8080:80 \
--env-file .env.production \
ghcr.io/malpercio-dev/atbb:v1.0.0
Zero-Downtime Upgrades (Future)#
Once Redis session storage is implemented, you can upgrade with zero downtime:
- Start new container on different port
- Test new version
- Switch reverse proxy to new port
- Stop old container
Not currently supported because sessions are in-memory.
9. Troubleshooting#
Container Won't Start#
Symptom: Container exits immediately after starting
Diagnosis:
docker logs atbb
Common causes:
-
Missing environment variables
Error: DATABASE_URL is requiredSolution: Verify
.env.productionhas all required variables (see Section 3). -
Database connection failed
Error: connect ECONNREFUSEDSolution:
- Verify
DATABASE_URLis correct - Check firewall allows connections from container's IP
- Test connection manually:
psql "postgresql://..."
- Verify
-
Port already in use
Error: bind: address already in useSolution: Change host port mapping:
-p 8081:80 -
Migrations not run
Error: relation "forums" does not existSolution: Run migrations (Section 4).
Database Connection Issues#
Symptom: Application starts but fails on database queries
Error examples:
FATAL: password authentication failed for user "atbb_user"
FATAL: no pg_hba.conf entry for host, SSL off
Error: connect ETIMEDOUT
Solutions:
-
Test connection manually:
psql "postgresql://atbb_user:PASSWORD@host:5432/atbb_prod?sslmode=require"If this fails, the issue is NOT with atBB (fix database access first).
-
Check credentials:
- Verify username/password in
DATABASE_URL - Ensure user has been created in database
- Verify username/password in
-
Check SSL settings:
# If database requires SSL, ensure connection string includes: DATABASE_URL=postgresql://...?sslmode=require -
Check network/firewall:
- Verify container can reach database host
- Test from within container:
docker exec atbb ping db.example.com - Check cloud provider security groups/firewall rules
OAuth Redirect URI Mismatch#
Symptom: Login fails with "redirect URI mismatch" error
Cause: OAUTH_PUBLIC_URL doesn't match the actual domain users access
Solution:
-
Verify
OAUTH_PUBLIC_URLin.env.production:OAUTH_PUBLIC_URL=https://forum.example.com # Must match actual domain -
Common mistakes:
- ❌
http://instead ofhttps://(use HTTPS in production) - ❌ Trailing slash:
https://forum.example.com/(remove trailing slash) - ❌ Wrong subdomain:
https://www.forum.example.comvshttps://forum.example.com
- ❌
-
Restart container after fixing:
docker restart atbb
PDS Connectivity Problems#
Symptom: Cannot create posts, forum metadata not syncing
Error in logs:
Error: Failed to connect to PDS: ENOTFOUND
Error: Invalid credentials for FORUM_HANDLE
Solutions:
-
Verify PDS URL:
curl https://bsky.social/xrpc/_health # Should return: {"version":"0.x.x"} -
Test forum credentials:
# Use atproto CLI or curl to test auth curl -X POST https://bsky.social/xrpc/com.atproto.server.createSession \ -H "Content-Type: application/json" \ -d '{ "identifier": "forum.example.com", "password": "YOUR_FORUM_PASSWORD" }' # Should return: {"did":"did:plc:...","accessJwt":"..."} -
Check environment variables:
docker exec atbb env | grep -E 'FORUM_|PDS_' # Verify all values are correct
High Memory Usage#
Symptom: Container using excessive memory (>1GB)
Diagnosis:
docker stats atbb
Solutions:
-
Set memory limit:
docker update --memory="512m" atbb -
Check for memory leak:
- Monitor over time:
docker stats atbb - If memory grows continuously, report issue with logs
- Monitor over time:
-
Increase container memory:
# For large forums, 1-2GB may be normal docker update --memory="2g" atbb
Logs Filling Disk#
Symptom: Disk space running out due to large log files
Check log size:
du -sh /var/lib/docker/containers/*/
Solutions:
-
Configure log rotation (recommended):
# Stop container docker stop atbb docker rm atbb # Restart with log rotation docker run -d \ --name atbb \ --log-opt max-size=10m \ --log-opt max-file=3 \ -p 8080:80 \ --env-file .env.production \ ghcr.io/malpercio-dev/atbb:latest -
Manually clean logs:
# Truncate logs (preserves container) truncate -s 0 $(docker inspect --format='{{.LogPath}}' atbb) -
Use external log aggregator (syslog, fluentd, etc.)
Container Performance Issues#
Symptom: Slow response times, high CPU usage
Diagnosis:
docker stats atbb
docker top atbb
Solutions:
-
Check database performance:
- Slow queries often bottleneck at database
- Monitor database server metrics
- Add indexes if needed (consult forum performance guide)
-
Increase resources:
docker update --cpus="2.0" --memory="1g" atbb -
Check reverse proxy settings:
- Ensure proxy is not buffering excessively
- Verify HTTP/2 is enabled for better performance
-
Monitor specific endpoints:
# Extract slow requests from logs docker logs atbb | grep '^{' | jq 'select(.duration > 1000)'
Session Errors / Random Logouts#
Symptom: Users randomly logged out, "session expired" errors
Causes:
- Container restarted — Sessions are in-memory, lost on restart
- SESSION_SECRET changed — Invalidates all sessions
- SESSION_SECRET not set — Each restart generates new secret
Solutions:
-
Verify SESSION_SECRET is set:
docker exec atbb env | grep SESSION_SECRET # Should show a 64-character hex string -
If blank, generate and set:
openssl rand -hex 32 # Add to .env.production # Restart container -
Future: Use Redis for persistent sessions (not yet implemented)
Getting Help#
If you cannot resolve an issue:
-
Collect diagnostics:
# Container logs docker logs atbb > atbb-logs.txt # Container info docker inspect atbb > atbb-inspect.json # Resource usage docker stats --no-stream atbb -
Sanitize sensitive data:
- Remove passwords from logs
- Remove
SESSION_SECRETfrom environment dumps
-
Report issue:
- GitHub Issues: https://github.com/malpercio-dev/atbb-monorepo/issues
- Include: atBB version, error messages, steps to reproduce
- Attach sanitized logs
10. Docker Compose Example#
For simpler local testing or single-server deployments, use Docker Compose.
File: docker-compose.example.yml (included in repository)
What It Provides#
- PostgreSQL database (local development)
- atBB application container
- Automatic dependency management (atBB waits for PostgreSQL)
- Volume persistence for database
- Health checks
Usage#
Step 1: Download files
# Download docker-compose.example.yml
curl -O https://raw.githubusercontent.com/malpercio-dev/atbb-monorepo/main/docker-compose.example.yml
# Download .env.production.example
curl -O https://raw.githubusercontent.com/malpercio-dev/atbb-monorepo/main/.env.production.example
# Rename to .env
mv .env.production.example .env
Step 2: Configure environment
# Generate session secret
openssl rand -hex 32
# Edit .env and fill in:
nano .env
Required changes in .env:
# AT Protocol credentials (from Prerequisites)
FORUM_DID=did:plc:YOUR_FORUM_DID
PDS_URL=https://bsky.social
FORUM_HANDLE=forum.example.com
FORUM_PASSWORD=YOUR_FORUM_PASSWORD
# OAuth (for local testing, use http://localhost)
OAUTH_PUBLIC_URL=http://localhost
# Session secret (generated above)
SESSION_SECRET=a1b2c3d4e5f6...
# Database connection will be set by docker-compose
# (Uses container name "postgres" as hostname)
Step 3: Start services
docker-compose -f docker-compose.example.yml up -d
Expected output:
Creating network "atbb_default" with the default driver
Creating volume "atbb_postgres_data" with default driver
Creating atbb-postgres ... done
Creating atbb-app ... done
Step 4: Run migrations
docker-compose -f docker-compose.example.yml exec atbb \
pnpm --filter @atbb/appview db:migrate
Step 5: Access forum
Visit: http://localhost
Management Commands#
View logs:
# All services
docker-compose -f docker-compose.example.yml logs -f
# Specific service
docker-compose -f docker-compose.example.yml logs -f atbb
docker-compose -f docker-compose.example.yml logs -f postgres
Stop services:
docker-compose -f docker-compose.example.yml down
Stop and remove data:
docker-compose -f docker-compose.example.yml down -v
# WARNING: This deletes the database volume!
Restart services:
docker-compose -f docker-compose.example.yml restart
Upgrade to new version:
# Pull new image
docker-compose -f docker-compose.example.yml pull atbb
# Run migrations (if required by release notes)
docker-compose -f docker-compose.example.yml exec atbb \
pnpm --filter @atbb/appview db:migrate
# Restart
docker-compose -f docker-compose.example.yml restart atbb
Production Considerations#
DO NOT use docker-compose.example.yml as-is in production.
Limitations:
- Database password is weak (change in compose file)
- No TLS/SSL for database
- No backups configured
- Single-server only
For production:
- Use managed PostgreSQL (AWS RDS, DigitalOcean, etc.)
- Run atBB container separately (not with local PostgreSQL)
- Set up reverse proxy with HTTPS (Caddy/nginx)
- Use strong passwords and secrets
- Configure automated backups
- Set up monitoring and alerting
Modified compose for production (atBB only, external DB):
version: '3.8'
services:
atbb:
image: ghcr.io/malpercio-dev/atbb:v1.0.0
container_name: atbb
restart: unless-stopped
ports:
- "127.0.0.1:8080:80" # Bind to localhost only
env_file:
- .env.production
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/api/healthz"]
interval: 30s
timeout: 3s
retries: 3
Appendix: Quick Reference#
Required Environment Variables#
# PostgreSQL (production recommended)
DATABASE_URL=postgresql://user:pass@host:5432/db?sslmode=require
# SQLite (lightweight alternative)
# DATABASE_URL=file:./atbb.db
FORUM_DID=did:plc:xxxxxxxxxxxxx
PDS_URL=https://bsky.social
FORUM_HANDLE=forum.example.com
FORUM_PASSWORD=strong_password_16+_chars
OAUTH_PUBLIC_URL=https://forum.example.com
SESSION_SECRET=64_hex_chars_from_openssl_rand
Essential Commands#
# Pull image
docker pull ghcr.io/malpercio-dev/atbb:latest
# Run migrations
docker run --rm --env-file .env.production \
ghcr.io/malpercio-dev/atbb:latest \
pnpm --filter @atbb/appview db:migrate
# Start container
docker run -d --name atbb --restart unless-stopped \
-p 8080:80 --env-file .env.production \
ghcr.io/malpercio-dev/atbb:latest
# View logs
docker logs -f atbb
# Stop/restart
docker stop atbb
docker restart atbb
# Health check
curl http://localhost:8080/api/healthz
Support Resources#
- Documentation: https://github.com/malpercio-dev/atbb-monorepo/tree/main/docs
- Issues: https://github.com/malpercio-dev/atbb-monorepo/issues
- Releases: https://github.com/malpercio-dev/atbb-monorepo/releases
- AT Protocol Docs: https://atproto.com/docs
Security Checklist#
Before going to production:
- Generated
SESSION_SECRETwithopenssl rand -hex 32 - Used strong, unique passwords (minimum 16 characters)
- Enabled database SSL/TLS (
?sslmode=require) - Set
OAUTH_PUBLIC_URLto HTTPS domain (not HTTP) - Set file permissions:
chmod 600 .env.production - Never committed
.env.productionto version control - Configured reverse proxy with HTTPS (Caddy/nginx)
- Set up database backups
- Configured log rotation
- Set up health monitoring
- Restricted firewall to ports 80/443 only
- Tested backup restoration procedure
11. NixOS Deployment#
The atBB flake provides a NixOS module that manages all services declaratively:
atbb-appview— Hono API server (systemd service)atbb-web— Hono web UI server (systemd service)atbb-migrate— One-shot database migration service- PostgreSQL 17 — Local database with peer authentication (optional)
- nginx — Reverse proxy with automatic ACME/Let's Encrypt TLS (optional)
The module is suitable for single-server deployments. Sections 1–10 of this guide describe Docker-based deployment; this section covers the NixOS path exclusively.
Step 1: Add atBB as a Flake Input#
In your NixOS system flake:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
atbb.url = "github:malpercio-dev/atbb-monorepo";
};
outputs = { nixpkgs, atbb, ... }: {
nixosConfigurations.my-server = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
atbb.nixosModules.default
./configuration.nix
];
};
};
}
Note: The module is exported as
nixosModules.default, notnixosModules.atbb.
Step 2: Create the Environment File#
The module reads secrets from an environment file on the server (never bake secrets into the Nix store). Create the file at a path of your choosing — /etc/atbb/env is a reasonable default:
sudo mkdir -p /etc/atbb
sudo tee /etc/atbb/env > /dev/null <<'EOF'
# Database — Unix socket peer auth (matches services.atbb.user = "atbb")
DATABASE_URL=postgres:///atbb?host=/run/postgresql
# PGHOST makes postgres.js use the Unix socket directory reliably,
# since it does not always honour the ?host= query parameter in URLs.
PGHOST=/run/postgresql
# Session security
SESSION_SECRET=<generate with: openssl rand -hex 32>
# Forum AT Protocol account credentials
FORUM_HANDLE=forum.example.com
FORUM_PASSWORD=<your forum account password>
EOF
sudo chmod 600 /etc/atbb/env
Why Unix socket for DATABASE_URL?
When database.enable = true (the default), the module creates a local PostgreSQL 17 instance and configures peer authentication. Peer auth maps the OS user name to the database user name — no password needed. The connection string postgres:///atbb?host=/run/postgresql says: connect to the atbb database via the Unix socket at /run/postgresql, as the current OS user (atbb).
Secrets management: For automated deployments, consider sops-nix or agenix to provision /etc/atbb/env as an encrypted secret rather than managing it manually.
Step 3: Configure the Module#
Add to your configuration.nix:
{
services.atbb = {
enable = true;
domain = "forum.example.com";
forumDid = "did:plc:your-forum-did";
pdsUrl = "https://bsky.social";
# Path to the environment file created in Step 2
environmentFile = /etc/atbb/env;
# Local PostgreSQL (default: true)
# Set to false to use an external database via DATABASE_URL
database.enable = true;
# Run migrations manually after each deploy (safer)
# Set to true to run automatically on every appview start
autoMigrate = false;
};
# Required when enableACME = true (the default)
security.acme = {
acceptTerms = true;
defaults.email = "admin@example.com";
};
}
Important: When database.enable = true, the system user name (services.atbb.user, default "atbb") must match the database name (services.atbb.database.name, default "atbb"). PostgreSQL peer authentication requires this. The module enforces this with an assertion — if you change either value, change both to match.
Key Options Reference#
| Option | Default | Description |
|---|---|---|
domain |
(required) | Public domain for the forum |
forumDid |
(required) | Forum's AT Protocol DID |
pdsUrl |
(required) | URL of the forum's PDS |
environmentFile |
(required) | Path to secrets file |
database.enable |
true |
Provision local PostgreSQL 17 |
database.name |
"atbb" |
Database name |
autoMigrate |
false |
Run migrations on appview start |
enableNginx |
true |
Configure nginx reverse proxy |
enableACME |
true |
Enable Let's Encrypt TLS |
appviewPort |
3000 |
Internal port for appview |
webPort |
3001 |
Internal port for web UI |
user / group |
"atbb" |
System user/group for services |
Step 4: Deploy#
Apply your configuration using your preferred NixOS deployment tool:
# Local rebuild
sudo nixos-rebuild switch --flake .#my-server
# Remote via colmena
colmena apply --on my-server
# Remote via nixos-rebuild
nixos-rebuild switch --flake .#my-server \
--target-host root@forum.example.com
Step 5: Run Database Migrations#
The atbb-migrate service is a one-shot systemd unit — it runs once and exits. Trigger it manually after each deployment:
sudo systemctl start atbb-migrate
# Check migration output
sudo journalctl -u atbb-migrate
Expected output:
Reading migrations from /nix/store/.../apps/appview/drizzle
Applying migration: 0000_initial_schema.sql
...
All migrations applied successfully
If you prefer migrations to run automatically on every appview start, set autoMigrate = true. Be aware this adds startup latency and prevents appview from starting if migrations fail.
Step 6: Verify Services#
# Check all atBB services
systemctl status atbb-appview atbb-web
# View live logs
journalctl -fu atbb-appview
journalctl -fu atbb-web
# Test the API
curl http://localhost:3000/api/healthz
# Expected: {"status":"ok"}
# Verify nginx is routing correctly
curl https://forum.example.com/api/healthz
Using Caddy Instead of nginx#
If you prefer Caddy, disable the built-in nginx proxy and configure services.caddy yourself:
{
services.atbb = {
# ... other options
enableNginx = false; # disable built-in nginx virtualHost
enableACME = false; # Caddy manages TLS automatically
};
services.caddy = {
enable = true;
virtualHosts."forum.example.com".extraConfig = ''
# AT Protocol well-known endpoints → appview
# Must reach appview (not web UI) for OAuth to work
handle /.well-known/* {
reverse_proxy localhost:${toString config.services.atbb.appviewPort}
}
# REST API → appview
handle /api/* {
reverse_proxy localhost:${toString config.services.atbb.appviewPort}
}
# Web UI — catch-all
handle {
reverse_proxy localhost:${toString config.services.atbb.webPort}
}
'';
};
}
See nix/Caddyfile.example in the repository for the equivalent standalone Caddyfile.
Upgrading#
To upgrade atBB, update the flake input and redeploy:
# Update atBB to latest
nix flake update atbb
# Redeploy
sudo nixos-rebuild switch --flake .#my-server
# Run migrations for the new version
sudo systemctl start atbb-migrate
NixOS handles the service restart automatically when the package changes. Because atbb-appview and atbb-web are declared with Restart = "on-failure", a failed startup will not leave broken processes running.
End of Deployment Guide
For questions or issues not covered here, please open an issue at: https://github.com/malpercio-dev/atbb-monorepo/issues