Skip to main content
TrailBase provides official Docker images for easy containerized deployment. The images are multi-arch and support both x86_64 and ARM64 architectures.

Docker Image

The official TrailBase Docker image is available on Docker Hub:
docker pull trailbase/trailbase:latest
Image details:
  • Base: Alpine Linux 3.22
  • User: Runs as unprivileged trailbase user
  • Architectures: linux/amd64, linux/arm64
  • Entrypoint: Uses tini for proper signal handling
  • Health check: Built-in health check on /api/healthcheck

Quick Start

Run TrailBase in a Docker container with persistent storage:
# Create data directory with proper permissions
mkdir -p traildepot

# Run TrailBase
docker run -d \
  --name trailbase \
  -p 4000:4000 \
  -v "$(pwd)/traildepot:/app/traildepot" \
  -e RUST_BACKTRACE=1 \
  --restart unless-stopped \
  trailbase/trailbase:latest

# Check logs for admin credentials
docker logs trailbase
Docker will create the traildepot directory with root ownership if it doesn’t exist. The TrailBase container runs as an unprivileged user and will encounter permission errors. Always create the directory first with proper permissions.

Docker Compose

The recommended way to run TrailBase with Docker is using docker-compose:
1

Create docker-compose.yml

Create a docker-compose.yml file based on the official template:
docker-compose.yml
services:
  trail:
    image: docker.io/trailbase/trailbase:latest
    ports:
      - "${PORT:-4000}:4000"
    restart: unless-stopped
    volumes:
      # Mount the data directory
      - ${DATA_DIR:-.}/traildepot:/app/traildepot
    environment:
      RUST_BACKTRACE: "1"
    # Optional: override the default command
    # command: "/app/trail --data-dir /app/traildepot run --address 0.0.0.0:4000"
2

Create data directory

Create the data directory with proper permissions:
mkdir -p traildepot
chown -R $(id -u):$(id -g) traildepot
3

Start the service

docker-compose up -d
4

View logs

docker-compose logs -f trail
On first start, the logs will contain the admin credentials.
The docker-compose file uses environment variables for easy customization. Set PORT to change the exposed port and DATA_DIR to change the data directory location.

Configuration Options

Environment Variables

Configure TrailBase through environment variables in docker-compose:
docker-compose.yml
services:
  trail:
    image: docker.io/trailbase/trailbase:latest
    ports:
      - "4000:4000"
    environment:
      # Global options
      DATA_DIR: "/app/traildepot"
      PUBLIC_URL: "https://yourdomain.com"
      
      # Server options
      ADDRESS: "0.0.0.0:4000"
      ADMIN_ADDRESS: "0.0.0.0:4001"
      SPA: "true"
      
      # Development
      RUST_BACKTRACE: "1"
      
      # Runtime options
      RUNTIME_THREADS: "4"
    volumes:
      - ./traildepot:/app/traildepot

Custom Command

Override the default command for advanced configuration:
docker-compose.yml
services:
  trail:
    image: docker.io/trailbase/trailbase:latest
    command: >
      /app/trail
        --data-dir /app/traildepot
        --public-url https://yourdomain.com
        run
        --address 0.0.0.0:4000
        --spa
        --public-dir /app/public
    volumes:
      - ./traildepot:/app/traildepot
      - ./public:/app/public:ro

Volume Mounts

Mount additional volumes for static files or WASM components:
docker-compose.yml
services:
  trail:
    image: docker.io/trailbase/trailbase:latest
    volumes:
      # Data directory (required)
      - ./traildepot:/app/traildepot
      
      # Static files for SPA
      - ./dist:/app/public:ro
      
      # Custom WASM components
      - ./wasm:/app/traildepot/wasm:ro
      
      # GeoIP database
      - ./GeoLite2-City.mmdb:/app/geoip.mmdb:ro
    environment:
      PUBLIC_DIR: "/app/public"
      SPA: "true"
      GEOIP_DB_PATH: "/app/geoip.mmdb"

Kubernetes Deployment

For Kubernetes deployments, TrailBase provides example manifests:
1

Create PersistentVolumeClaim

trailbase-storage.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: trailbase-storage
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
2

Create Deployment

trailbase-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: trailbase-deployment
  labels:
    app: trailbase
spec:
  replicas: 1
  selector:
    matchLabels:
      app: trailbase
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: trailbase
    spec:
      containers:
        - name: trailbase
          image: docker.io/trailbase/trailbase:latest
          ports:
          - containerPort: 4000
          env:
          - name: RUST_BACKTRACE
            value: "1"
          volumeMounts:
            - name: trailbase-storage
              mountPath: /app/traildepot
      restartPolicy: Always
      volumes:
        - name: trailbase-storage
          persistentVolumeClaim:
            claimName: trailbase-storage
3

Create Service

trailbase-service.yml
apiVersion: v1
kind: Service
metadata:
  name: trailbase-service
spec:
  selector:
    app: trailbase
  ports:
    - protocol: TCP
      port: 4000
      targetPort: 4000
4

Deploy to cluster

kubectl apply -f trailbase-storage.yml
kubectl apply -f trailbase-deployment.yml
kubectl apply -f trailbase-service.yml
The Kubernetes deployment uses a Recreate strategy to ensure only one pod writes to the database at a time, as SQLite does not support concurrent writes from multiple processes.

Podman Support

TrailBase images also work with Podman:
# Using Podman play kube with the deployment manifest
podman play kube trailbase-deployment.yml --publish=4010:4000

# Check running containers
podman ps

# View logs
podman logs trailbase-deployment-pod-trailbase
For persistent storage with Podman:
# Create a named volume
podman volume create trailbase-storage

# Or use a bind mount with proper permissions
podman play kube trailbase-deployment.yml --userns=keep-id

Building Custom Images

To build a custom TrailBase Docker image:
1

Clone the repository

git clone https://github.com/trailbaseio/trailbase.git
cd trailbase
git submodule update --init --recursive
2

Build the image

docker build -t trailbase:custom .
The Dockerfile uses multi-stage builds:
  • builder: Compiles Rust code with MUSL for static linking
  • auth-ui-builder: Builds auth UI WASM component
  • binary-builder: Builds the main TrailBase binary
  • image: Final Alpine-based image with minimal dependencies
3

Run your custom image

docker run -d -p 4000:4000 -v ./traildepot:/app/traildepot trailbase:custom

Health Checks

The Docker image includes a built-in health check:
HEALTHCHECK CMD curl --fail http://localhost:4000/api/healthcheck || exit 1
Use this in docker-compose:
docker-compose.yml
services:
  trail:
    image: docker.io/trailbase/trailbase:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:4000/api/healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
Or override it:
services:
  trail:
    image: docker.io/trailbase/trailbase:latest
    healthcheck:
      disable: true

Production Recommendations

Avoid using :latest in production. Pin to specific versions:
services:
  trail:
    image: docker.io/trailbase/trailbase:v0.1.0
Define CPU and memory limits:
services:
  trail:
    image: docker.io/trailbase/trailbase:latest
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 256M
Configure log rotation:
services:
  trail:
    image: docker.io/trailbase/trailbase:latest
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
Store sensitive configuration in Docker secrets:
services:
  trail:
    image: docker.io/trailbase/trailbase:latest
    secrets:
      - smtp_password
    environment:
      TRAIL_EMAIL_SMTP_PASSWORD_FILE: /run/secrets/smtp_password

secrets:
  smtp_password:
    external: true

Troubleshooting

Permission Denied Errors

If you see “Permission denied” errors in logs:
# Fix ownership of data directory
sudo chown -R 1000:1000 traildepot/

# Or run container as root (not recommended)
docker run --user root trailbase/trailbase:latest

Container Won’t Start

Check logs for errors:
docker logs trailbase
# or
docker-compose logs trail

Database Locked

SQLite databases can’t be accessed by multiple containers:
# Ensure only one container is running
docker ps | grep trailbase

# Stop all TrailBase containers
docker stop $(docker ps -q --filter ancestor=trailbase/trailbase)

Next Steps

Production Setup

Production checklist, security hardening, and monitoring

Configuration

Detailed configuration options and environment variables

Build docs developers (and LLMs) love