Back to Blog
tutorialhomelabdockerself-hosteddeployment

Deploy Burning Ash Protocol on Your Homelab in 15 Minutes

AK
Abel Kuruvilla
10 min read
Share on X
Deploy Burning Ash Protocol on Your Homelab in 15 Minutes

Deploy Burning Ash Protocol on Your Homelab in 15 Minutes

This tutorial walks you through deploying Burning Ash Protocol (BAP) on your homelab using Docker Compose. By the end, you will have a fully functional, self-hosted digital will system with HTTPS, accessible from the internet (or restricted to your local network, depending on your preference).

What you will have after completing this tutorial:

  • BAP API running on your homelab server
  • BAP web dashboard accessible via HTTPS
  • Reverse proxy handling TLS termination
  • Automatic database migrations
  • A working admin account ready for configuration

Prerequisites:

  • A Linux server (Debian, Ubuntu, or similar) with Docker and Docker Compose installed
  • A domain name pointed at your server (if you want external access with HTTPS)
  • Basic comfort with the command line
  • 15 minutes

Step 1: Clone the Repository

Start by cloning the BAP repository to your server. If you prefer a specific release, check the releases page on GitHub and check out the corresponding tag.

git clone https://github.com/baprotocol/bap.git
cd bap

The repository contains three services: the Go API (api/), the Next.js web frontend (web/), and the documentation site (documentation/). For a homelab deployment, you need the API and the web frontend.

Step 2: Generate Secrets and Configure Environment

BAP requires two cryptographic secrets: a JWT secret for authentication tokens and a Master Key for encrypting Data Encryption Keys. Both must be 256-bit (32-byte) random values.

Generate them using OpenSSL:

openssl rand -hex 32
# Copy this output — it becomes your JWT_SECRET

openssl rand -hex 32
# Copy this output — it becomes your MASTER_KEY

Now create your environment file from the example:

cp .env.example .env

Open .env in your editor and configure the essential variables:

# Required secrets
JWT_SECRET=<your-generated-jwt-secret>
MASTER_KEY=<your-generated-master-key>

# Database — SQLite is fine for a homelab
DB_TYPE=sqlite
DATABASE_PATH=./data/bap.db

# Deploy mode — selfhosted disables billing and platform connectors
DEPLOY_MODE=selfhosted

# CORS — must match the URL where you access the web frontend
CORS_ORIGINS=https://bap.yourdomain.com

# API URL for the frontend to call
NEXT_PUBLIC_API_URL=https://bap-api.yourdomain.com/api

# Admin bootstrap — set a temporary secret to create the first admin account
ADMIN_BOOTSTRAP_SECRET=<a-temporary-strong-secret>


Important: Store your MASTER_KEY securely outside the server as well. If you lose it, all encrypted wills become permanently inaccessible. Write it down and store it in a fireproof safe alongside your other critical backups.

Step 3: Docker Compose Configuration

BAP includes a Docker Compose file for development. For a homelab production deployment, you may want to customize it. Here is a production-oriented configuration:

# docker-compose.yml
version: "3.8"

services:
  api:
    build:
      context: ./api
      dockerfile: Dockerfile
    container_name: bap-api
    restart: unless-stopped
    env_file: .env
    volumes:
      - bap-data:/app/data
    ports:
      - "127.0.0.1:8080:8080"
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  web:
    build:
      context: ./web
      dockerfile: Dockerfile
    container_name: bap-web
    restart: unless-stopped
    env_file: .env
    ports:
      - "127.0.0.1:3000:3000"
    depends_on:
      api:
        condition: service_healthy

volumes:
  bap-data:
    driver: local

Note that both services bind to 127.0.0.1 only. This ensures they are not directly accessible from the network --- all external traffic goes through the reverse proxy.

Step 4: Set Up a Reverse Proxy

A reverse proxy serves two purposes: it terminates TLS (HTTPS) and routes requests to the correct service based on the domain name.

You have two good options: Caddy (simpler, automatic HTTPS) or Nginx (more configurable, widely documented).

Caddy automatically obtains and renews Let's Encrypt certificates. This is the simplest path to HTTPS.

Create a Caddyfile in your project root:

bap.yourdomain.com {
    reverse_proxy localhost:3000
}

bap-api.yourdomain.com {
    reverse_proxy localhost:8080
}

Add Caddy to your Docker Compose file:

  caddy:
    image: caddy:2-alpine
    container_name: bap-caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy-data:/data
      - caddy-config:/config
    depends_on:
      - api
      - web

Add the Caddy volumes to the volumes section:

volumes:
  bap-data:
    driver: local
  caddy-data:
    driver: local
  caddy-config:
    driver: local

That is it. Caddy handles certificate issuance, renewal, and HTTPS redirection automatically.

Option B: Nginx with Certbot

If you prefer Nginx, create an Nginx configuration file:

# /etc/nginx/sites-available/bap

server {
    listen 80;
    server_name bap.yourdomain.com bap-api.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name bap.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/bap.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/bap.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}

server {
    listen 443 ssl http2;
    server_name bap-api.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/bap-api.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/bap-api.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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;
    }
}

Obtain certificates with Certbot:

sudo certbot --nginx -d bap.yourdomain.com -d bap-api.yourdomain.com

Step 5: Configure DNS

Point your domain names to your server's IP address. If your homelab is behind a home router, you will need to set up port forwarding for ports 80 and 443.

For external access:

Create A records in your DNS provider:

bap.yourdomain.com       A    <your-public-ip>
bap-api.yourdomain.com   A    <your-public-ip>

Configure your router to forward ports 80 and 443 to your homelab server's local IP address.

For local-only access:

If you do not want your digital will accessible from the internet (a valid security choice), use local DNS resolution instead. Add entries to your local DNS server (Pi-hole, AdGuard Home, or similar):

bap.yourdomain.com       A    192.168.1.x
bap-api.yourdomain.com   A    192.168.1.x

For local-only HTTPS with Caddy, you can use Caddy's internal TLS feature:

bap.yourdomain.com {
    tls internal
    reverse_proxy localhost:3000
}

bap-api.yourdomain.com {
    tls internal
    reverse_proxy localhost:8080
}

This generates self-signed certificates. You will need to trust Caddy's root CA on your devices to avoid browser warnings.

Step 6: Start the Stack

Build and start all services:

docker compose up -d --build

Watch the logs to confirm everything starts correctly:

docker compose logs -f

You should see:

  • The API service running migrations and starting on port 8080
  • The web frontend building and starting on port 3000
  • Caddy (if using it) obtaining TLS certificates

Once all services are healthy, open your browser and navigate to https://bap.yourdomain.com. You should see the BAP login page.

Step 7: Create the Admin Account

BAP uses a bootstrap endpoint to create the first super-admin account. This endpoint is protected by the ADMIN_BOOTSTRAP_SECRET you configured in Step 2.

curl -X POST https://bap-api.yourdomain.com/api/admin/bootstrap \
  -H "Content-Type: application/json" \
  -d '{
    "email": "admin@yourdomain.com",
    "password": "your-strong-admin-password",
    "secret": "<your-ADMIN_BOOTSTRAP_SECRET>"
  }'

After creating the admin account, remove or change the ADMIN_BOOTSTRAP_SECRET in your .env file and restart the API:

docker compose restart api

This prevents anyone else from creating admin accounts using the bootstrap endpoint.

Step 8: Verify the Deployment

Log in to the web dashboard at https://bap.yourdomain.com using your admin credentials. Verify that:

  1. You can log in successfully
  2. The dashboard loads without errors
  3. You can navigate to the will management section
  4. You can access the settings page

Create a test will to verify the full encryption pipeline works:

  1. Create a new will with a test document
  2. Add a test Survivor (use your own email or phone number)
  3. Configure liveness check parameters
  4. Verify the Survivor receives a notification

Step 9: Configure Notification Connectors

For the dead man's switch to function, BAP needs at least one notification channel configured. In self-hosted mode, you provide your own credentials (BYO connectors).

Email (SMTP)

The most essential connector. Configure your SMTP credentials in the dashboard under Settings > Connectors:

  • SMTP Host: Your mail server (e.g., smtp.gmail.com, smtp.fastmail.com, or your own mail server)
  • SMTP Port: 587 (TLS) or 465 (SSL)
  • Username: Your email address
  • Password: An app-specific password (never your main email password)

If you run your own mail server, use that. If you use Gmail, generate an app-specific password in your Google Account security settings.

SMS (Twilio)

For SMS notifications, you need a Twilio account:

  • Account SID: Found in your Twilio console
  • Auth Token: Found in your Twilio console
  • From Number: Your Twilio phone number

Telegram

For Telegram notifications, create a bot via @BotFather and configure the bot token in BAP.

Step 10: Backup Strategy

Your BAP deployment stores encrypted data that by definition cannot be recreated. A backup strategy is essential.

Database Backup

For SQLite, the database is a single file. Back it up regularly:

# Add to crontab: daily backup at 2 AM
0 2 * * * docker compose exec api sqlite3 /app/data/bap.db ".backup '/app/data/backup-$(date +\%Y\%m\%d).db'"

Copy backups to a separate location (a different physical drive, NAS, or encrypted cloud storage).

Master Key Backup

Your Master Key is the most critical secret. If it is lost, all encrypted wills are permanently inaccessible.

  • Write it on paper and store it in a fireproof safe
  • Store it in a separate password manager vault
  • Consider splitting it using Shamir's Secret Sharing (yes, you can use the same technique for your own key)

Configuration Backup

Back up your .env file and Docker Compose configuration. Store them separately from the database backups.

Maintenance

Updates

When new BAP releases are available:

cd bap
git pull origin main
docker compose up -d --build

Database migrations run automatically at API startup. Check the logs after updating to confirm migrations completed successfully.

Monitoring

For a homelab, basic monitoring ensures you know if the service goes down:

  • Use Uptime Kuma, Healthchecks.io, or similar to monitor the /api/health endpoint
  • Configure alerts so you know if the service is unreachable
  • Check Docker logs periodically for errors

This is especially important for a dead man's switch: if the server is down, liveness checks are not being sent, and the switch cannot trigger.

Security Hardening

For a production homelab deployment, consider these additional security measures:

Firewall rules. Only expose ports 80 and 443. Block all other incoming traffic.

sudo ufw default deny incoming
sudo ufw allow 22/tcp    # SSH
sudo ufw allow 80/tcp    # HTTP (redirect to HTTPS)
sudo ufw allow 443/tcp   # HTTPS
sudo ufw enable

Fail2ban. Protect against brute-force attacks on your reverse proxy and SSH.

Automatic updates. Enable unattended security updates for your OS.

Separate user. Run Docker as a non-root user. Do not run BAP services as root inside containers.

VPN access. If your BAP instance is local-only, access it via WireGuard or Tailscale from outside your network rather than exposing ports to the internet.

Troubleshooting

"Connection refused" when accessing the web interface: Check that all containers are running with docker compose ps. Verify that your reverse proxy is running and correctly configured. Check that DNS is resolving to the correct IP address.

"CORS error" in the browser console: Ensure CORS_ORIGINS in your .env file exactly matches the URL you use to access the frontend, including the protocol (https://) and port if non-standard.

"Unauthorized" errors when calling the API: Verify that NEXT_PUBLIC_API_URL in your .env file is correct and accessible from your browser. This is a client-side setting --- the browser calls this URL directly, not the server.

Database migration errors: Check the API logs with docker compose logs api. If a migration fails, the API will not start. Common causes include file permission issues on the data volume.

Conclusion

You now have a fully self-hosted Burning Ash Protocol instance running on your homelab. Your digital will data is encrypted with AES-256-GCM, protected by keys you control, and accessible only through your infrastructure. No third party can access, modify, or shut down your digital estate plan.

The next steps are to configure your actual will content, add your Survivors, set appropriate liveness check intervals, and test the full flow end-to-end with a test Survivor before considering your deployment production-ready.

Related Articles