Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/iamngoni/heimdall/llms.txt

Use this file to discover all available pages before exploring further.

This guide covers production-ready deployment of Heimdall with security hardening, monitoring, and backup strategies.

Pre-Deployment Checklist

1

Generate secure keys

# Encryption key (32 bytes)
openssl rand -hex 32

# Webhook secret (20 bytes)
openssl rand -hex 20
Store these in your .env file.
2

Configure AI providers

Set at least one API key:
ANTHROPIC_API_KEY=sk-ant-...
# or OPENAI_API_KEY=sk-...
# or OLLAMA_URL=http://localhost:11434
3

Set production database

Use a managed database service or a dedicated PostgreSQL instance:
DATABASE_URL=postgres://heimdall:secure_password@db.example.com:5432/heimdall
4

Configure TLS

Either:
  • Use a reverse proxy (recommended) → set TLS_ENABLED=false
  • Or enable TLS in Heimdall → set TLS_ENABLED=true and provide certificates
5

Update CORS origin

CORS_ALLOWED_ORIGIN=https://heimdall.example.com

Building the Release Binary

Option 1: Native Build

# Install Rust (if not already installed)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default stable

# Clone repository
git clone https://github.com/modestnerd/heimdall.git
cd heimdall

# Build optimized release binary
cargo build --release

# Binary location
ls -lh target/release/heimdall

Option 2: Docker Build

# Build Docker image
docker compose build --build-arg DB_DRIVER=postgres

# Or use multi-platform builds
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --build-arg DB_DRIVER=postgres \
  -t heimdall:latest .
The Docker build automatically handles schema generation and migration compilation.

Database Setup

1

Install PostgreSQL

# Ubuntu/Debian
sudo apt update
sudo apt install postgresql-17 postgresql-contrib

# macOS
brew install postgresql@17
2

Create database and user

sudo -u postgres psql
CREATE DATABASE heimdall;
CREATE USER heimdall WITH ENCRYPTED PASSWORD 'your_secure_password';
GRANT ALL PRIVILEGES ON DATABASE heimdall TO heimdall;
\q
3

Configure connection pooling (optional)

Use PgBouncer for connection pooling:
sudo apt install pgbouncer
Edit /etc/pgbouncer/pgbouncer.ini:
[databases]
heimdall = host=localhost port=5432 dbname=heimdall

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = md5
pool_mode = transaction
max_client_conn = 100
default_pool_size = 20
4

Test connection

psql "postgres://heimdall:your_secure_password@localhost:5432/heimdall" -c "SELECT 1;"

Managed Database Services

Heimdall works with managed PostgreSQL services:
DATABASE_URL=postgres://heimdall:password@heimdall.abc123.us-east-1.rds.amazonaws.com:5432/heimdall
Always use SSL/TLS for database connections in production by adding ?sslmode=require to your connection string.

Security Hardening

1. Environment Variables

Never commit .env files. Use secrets management:
# Create secrets
echo "your_encryption_key" | docker secret create encryption_key -
echo "your_webhook_secret" | docker secret create webhook_secret -

# Reference in docker-compose.yml
secrets:
  - encryption_key
  - webhook_secret

2. Network Security

# Firewall rules (UFW example)
sudo ufw allow 22/tcp   # SSH
sudo ufw allow 443/tcp  # HTTPS
sudo ufw deny 8080/tcp  # Block direct access to Heimdall
sudo ufw enable

# Only allow reverse proxy to access Heimdall
APP_HOST=127.0.0.1  # Bind to localhost only
APP_PORT=8080

3. Docker Socket Security

Heimdall requires Docker socket access for the Garmr sandbox. This is a privileged operation.
Minimize risk:
# docker-compose.yml
services:
  heimdall:
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro  # Read-only mount
    user: "1000:999"  # Non-root user in docker group
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE

4. Database Security

# PostgreSQL authentication (pg_hba.conf)
# Require SSL for remote connections
hostssl all all 0.0.0.0/0 scram-sha-256

# Rotate passwords regularly
ALTER USER heimdall WITH PASSWORD 'new_secure_password';

# Enable audit logging
ALTER DATABASE heimdall SET log_statement = 'mod';

5. Rate Limiting

Implement rate limiting at the reverse proxy level (see Reverse Proxy).

Running as a System Service

Create a systemd service for automatic startup and restart:
# /etc/systemd/system/heimdall.service
[Unit]
Description=Heimdall Security Scanner
After=network.target postgresql.service
Requires=postgresql.service

[Service]
Type=simple
User=heimdall
Group=heimdall
WorkingDirectory=/opt/heimdall
EnvironmentFile=/opt/heimdall/.env
ExecStart=/opt/heimdall/heimdall
Restart=on-failure
RestartSec=10
KillMode=mixed
KillSignal=SIGTERM

# Security
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/heimdall/repos

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=heimdall

[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable heimdall
sudo systemctl start heimdall
sudo systemctl status heimdall
View logs:
journalctl -u heimdall -f

Monitoring and Logging

Health Checks

Heimdall exposes a health endpoint:
curl http://localhost:8080/health
# {"status":"ok"}
Integrate with monitoring tools:
scrape_configs:
  - job_name: 'heimdall'
    metrics_path: '/health'
    static_configs:
      - targets: ['localhost:8080']

Application Logs

Configure structured logging:
# Detailed logging
RUST_LOG=info,heimdall=debug,actix_web=info

# Production logging (less verbose)
RUST_LOG=info,heimdall=info

# Error-only logging
RUST_LOG=error,heimdall=warn
Ship logs to aggregation services:
# Install promtail
curl -s https://raw.githubusercontent.com/grafana/loki/main/tools/promtail.sh | bash

# Configure promtail.yml
clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: heimdall
    journal:
      labels:
        job: heimdall

Database Monitoring

-- Monitor active connections
SELECT count(*) FROM pg_stat_activity WHERE datname = 'heimdall';

-- Check slow queries
SELECT pid, now() - query_start as duration, query
FROM pg_stat_activity
WHERE state = 'active' AND now() - query_start > interval '5 seconds';

-- Table sizes
SELECT 
  schemaname,
  tablename,
  pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;

Backup Strategies

Database Backups

1

Automated PostgreSQL backups

#!/bin/bash
# /opt/scripts/backup-heimdall.sh

BACKUP_DIR="/backups/heimdall"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# Create backup
pg_dump -h localhost -U heimdall -Fc heimdall > "$BACKUP_DIR/heimdall_$TIMESTAMP.dump"

# Compress
gzip "$BACKUP_DIR/heimdall_$TIMESTAMP.dump"

# Delete backups older than 30 days
find "$BACKUP_DIR" -name "*.dump.gz" -mtime +30 -delete

# Upload to S3 (optional)
aws s3 cp "$BACKUP_DIR/heimdall_$TIMESTAMP.dump.gz" s3://my-backups/heimdall/
2

Schedule with cron

# Daily backups at 2 AM
0 2 * * * /opt/scripts/backup-heimdall.sh >> /var/log/heimdall-backup.log 2>&1
3

Test restore procedure

# Restore from backup
gunzip < /backups/heimdall/heimdall_20260312_020000.dump.gz | \
  pg_restore -h localhost -U heimdall -d heimdall_test --clean --if-exists

Repository Data Backups

Cloned repositories are stored in the filesystem:
# Default location: /app/repos (in Docker) or ./repos (native)

# Backup script
#!/bin/bash
REPO_DIR="/opt/heimdall/repos"
BACKUP_DIR="/backups/heimdall-repos"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

tar -czf "$BACKUP_DIR/repos_$TIMESTAMP.tar.gz" -C "$REPO_DIR" .

# Cleanup old backups
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -delete
Repository data can be safely deleted — Heimdall will re-clone repositories when needed for new scans.

Scaling Considerations

Vertical Scaling

Minimum recommended resources:
ComponentCPURAMStorage
Heimdall2 cores4 GB20 GB
PostgreSQL2 cores4 GB50 GB
Total4 cores8 GB70 GB
For heavy usage (multiple concurrent scans):
ComponentCPURAMStorage
Heimdall4-8 cores8-16 GB50 GB
PostgreSQL4 cores8 GB200 GB

Worker Configuration

# Enable background worker
WORKER_ENABLED=true

# Poll interval for queued scans (seconds)
WORKER_POLL_INTERVAL_SECS=5

# Timeout for stale scans (minutes)
WORKER_STALE_TIMEOUT_MINS=10

Database Connection Pool

# Increase max connections in postgresql.conf
max_connections = 200

# Use PgBouncer for connection pooling
default_pool_size = 20
max_client_conn = 100

Updates and Maintenance

Updating Heimdall

1

Pull latest code

cd /opt/heimdall
git pull origin main
2

Rebuild

cargo build --release
# or
docker compose build
3

Restart service

sudo systemctl restart heimdall
# or
docker compose restart heimdall
Schema changes are applied automatically on startup.
4

Verify

curl http://localhost:8080/health
journalctl -u heimdall -n 50

Zero-Downtime Updates

For critical deployments:
  1. Run two instances behind a load balancer
  2. Update one instance at a time
  3. Use health checks to route traffic only to healthy instances

Troubleshooting

High CPU usage

Check for:
  • Multiple concurrent scans (review scans table)
  • Stuck worker processes (ps aux | grep heimdall)
  • Reduce WORKER_POLL_INTERVAL_SECS

High memory usage

Check for:
  • Large repositories being scanned
  • Memory leaks in AI provider connections (check logs)
  • Increase available RAM or limit concurrent scans

Database connection errors

# Check PostgreSQL is running
sudo systemctl status postgresql

# Test connection
psql $DATABASE_URL -c "SELECT 1;"

# Check connection limits
SELECT count(*) FROM pg_stat_activity;

Scan failures

Check logs:
journalctl -u heimdall | grep ERROR
Common issues:
  • AI provider API key invalid/expired
  • Docker socket permission denied
  • Insufficient disk space for cloning repos

Next Steps

Reverse Proxy Setup

Configure Nginx with TLS and SSE support

Configuration Reference

Complete list of environment variables