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:
Create docker-compose.yml
Create a docker-compose.yml file based on the official template: 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"
Create data directory
Create the data directory with proper permissions: mkdir -p traildepot
chown -R $( id -u ) : $( id -g ) traildepot
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:
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:
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:
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:
Create PersistentVolumeClaim
apiVersion : v1
kind : PersistentVolumeClaim
metadata :
name : trailbase-storage
spec :
accessModes :
- ReadWriteOnce
resources :
requests :
storage : 10Gi
Create Deployment
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
Create Service
apiVersion : v1
kind : Service
metadata :
name : trailbase-service
spec :
selector :
app : trailbase
ports :
- protocol : TCP
port : 4000
targetPort : 4000
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:
Clone the repository
git clone https://github.com/trailbaseio/trailbase.git
cd trailbase
git submodule update --init --recursive
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
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:
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
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"
Use secrets for sensitive data
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