Self-Hosting

heaper can be self-hosted, giving you complete control over your data and infrastructure.

Prerequisites

  • Docker
  • Domain (recommended for TLS via a reverse proxy like Caddy or nginx)

Platform Architecture

The heaper image is built for x64 (amd64) and ARM64 (aarch64) devices.

Docker run

docker run -d --name heaper-selfhost \
  -p 3010:80 \
  # ... other options
  ghcr.io/janlunge/heaper:latest

Quick Start

Create a docker-compose.yml:

services:
  heaper:
    image: ghcr.io/janlunge/heaper:latest
    platform: linux/amd64   # Required on ARM (Raspberry Pi, Apple Silicon)
    container_name: heaper
    restart: unless-stopped
    mem_limit: 4g
    memswap_limit: 4g
    ports:
      - "3010:80"
      # - "5432:5432"  # Uncomment to expose PostgreSQL externally
    environment:
      - HOSTNAME=localhost
      - POSTGRES_USER=heaper
      - POSTGRES_PASSWORD=change-me-please
      - POSTGRES_DB=heaper
      - ENABLE_INTERNAL_POSTGRES=true
    volumes:
      - ./heaper-data/postgres:/var/lib/postgresql/data
      - ./heaper-data/data:/usr/src/app/data
      - ./heaper-data/config:/usr/src/app/config
      - ./heaper-data/thumbnails:/mnt/thumbnails
      - ./heaper-data/storage:/mnt/storage
      - ./heaper-data/backups:/mnt/backups

Info: When using Podman instead of Docker, the Postgres volume (/var/lib/postgresql/data) cannot be bind-mounted to a host path — Postgres requires specific permissions that Podman does not allow for bind mounts. Use a named volume (e.g. postgres_data:/var/lib/postgresql/data) instead.

Change POSTGRES_PASSWORD to a secure password, then:

docker-compose up -d

Access: http://localhost:3010

For more details (docker run, environment variables, volumes) see the All-in-one setup guide.

Looking for a setup with a separate PostgreSQL container? See the Separate containers guide.

Updating

docker-compose pull
docker-compose up -d

Docker run: Pull the new image, remove the old container, then run your original docker run command again.

Nightly Test Builds

If you want to test a pre-release build before it goes live, replace ghcr.io/janlunge/heaper:latest with ghcr.io/janlunge/heaper:nightly.

Nightly is an opt-in testing channel for the combined self-hosted image. It may include unfinished changes and is less stable than latest.

Backup & Restore

Automated Backups

Heaper supports automated daily database backups. Backups run automatically when the /mnt/backups volume is mounted.

VariableDefaultDescription
BACKUP_ENCRYPTION_KEY(optional)Passphrase for AES-256-GCM encryption
BACKUP_RETENTION_DAYS7Number of days to keep backups
BACKUP_HOUR3Hour (UTC) to run daily backup (0-23)

Docker Compose: The compose file already mounts ./heaper-data/backups:/mnt/backups. Add to the service environment to enable encrypted backups:

- BACKUP_ENCRYPTION_KEY=your-secure-passphrase

Docker run — enable automated backups:

docker run -d --name heaper-selfhost \
  --volume /path/to/heaper/backups:/mnt/backups \
  # ... other options
  ghcr.io/janlunge/heaper:latest

Docker run — encrypted backups (recommended):

docker run -d --name heaper-selfhost \
  -e BACKUP_ENCRYPTION_KEY=your-secure-passphrase \
  --volume /path/to/heaper/backups:/mnt/backups \
  # ... other options
  ghcr.io/janlunge/heaper:latest

Backup filenames:

  • Unencrypted: heaper_backup_20240115_030000.sql
  • Encrypted: heaper_backup_20240115_030000.sql.enc

Manual Database Backup

All-in-one setup (container name heaper with compose, heaper-selfhost with docker run):

docker exec heaper pg_dump -U heaper heaper > backup.sql

Separate containers setup:

docker exec heaper-selfhost-postgres pg_dump -U heaper heaper > backup.sql

Restore Database

All-in-one setup:

docker exec -i heaper psql -U heaper heaper < backup.sql

Separate containers setup:

docker exec -i heaper-selfhost-postgres psql -U heaper heaper < backup.sql

Decrypt Backup (for encrypted backups)

Encrypted backups (.sql.enc) use AES-256-GCM with the nonce prepended. To restore:

  1. Unencrypted backups can be restored directly with psql
  2. Encrypted backups must be decrypted first (key derived via SHA-256 from passphrase)

File Backup

Back up your mounted volumes regularly:

  • /var/lib/postgresql/data — Database files
  • /usr/src/app/config — Configuration
  • /mnt/thumbnails — Thumbnails
  • /mnt/storage — User files
  • /mnt/backups — Encrypted database backups

Troubleshooting

Check Service Health

# Check container health (use heaper for compose, heaper-selfhost for docker run)
docker inspect --format='{{.State.Health.Status}}' heaper

# Backend API health (use 3010 for compose default)
curl http://localhost:3010/api

# Sync server health
curl http://localhost:3010/sync/health

# PostgreSQL health (single container / compose)
docker exec heaper pg_isready -h localhost -U heaper

View Logs

# All logs (container name: heaper with compose, heaper-selfhost with docker run)
docker logs -f heaper

# Follow specific service logs (via supervisor)
docker exec heaper tail -f /var/log/supervisor/*.log

Connect to PostgreSQL

With Docker Compose, PostgreSQL is not exposed by default. Uncomment - "5432:5432" in the compose file to expose it. Then:

# Using psql
psql -h localhost -p 5432 -U heaper -d heaper

# Using a GUI tool (TablePlus, pgAdmin, etc.)
# Host: localhost (or your server IP)
# Port: 5432
# User: heaper
# Password: your-secure-password
# Database: heaper

Connecting to Your Server

To use your self-hosted instance in the heaper app, first login to your cloud account then add a new server via Settings → Heaps → Pull Heap and enter the IP or hostname of your server.

Note: For TLS encryption, use a reverse proxy (e.g. Caddy, nginx) in front of heaper with a valid domain and certificate.