> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/trailbaseio/trailbase/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker Deployment

> Deploy TrailBase using Docker and docker-compose with persistent storage and configuration

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:

```bash theme={null}
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:

```bash theme={null}
# 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
```

<Warning>
  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.
</Warning>

## Docker Compose

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

<Steps>
  <Step title="Create docker-compose.yml">
    Create a `docker-compose.yml` file based on the official template:

    ```yaml docker-compose.yml theme={null}
    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"
    ```
  </Step>

  <Step title="Create data directory">
    Create the data directory with proper permissions:

    ```bash theme={null}
    mkdir -p traildepot
    chown -R $(id -u):$(id -g) traildepot
    ```
  </Step>

  <Step title="Start the service">
    ```bash theme={null}
    docker-compose up -d
    ```
  </Step>

  <Step title="View logs">
    ```bash theme={null}
    docker-compose logs -f trail
    ```

    On first start, the logs will contain the admin credentials.
  </Step>
</Steps>

<Info>
  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.
</Info>

## Configuration Options

### Environment Variables

Configure TrailBase through environment variables in docker-compose:

```yaml docker-compose.yml theme={null}
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:

```yaml docker-compose.yml theme={null}
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:

```yaml docker-compose.yml theme={null}
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:

<Steps>
  <Step title="Create PersistentVolumeClaim">
    ```yaml trailbase-storage.yml theme={null}
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: trailbase-storage
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 10Gi
    ```
  </Step>

  <Step title="Create Deployment">
    ```yaml trailbase-deployment.yml theme={null}
    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
    ```
  </Step>

  <Step title="Create Service">
    ```yaml trailbase-service.yml theme={null}
    apiVersion: v1
    kind: Service
    metadata:
      name: trailbase-service
    spec:
      selector:
        app: trailbase
      ports:
        - protocol: TCP
          port: 4000
          targetPort: 4000
    ```
  </Step>

  <Step title="Deploy to cluster">
    ```bash theme={null}
    kubectl apply -f trailbase-storage.yml
    kubectl apply -f trailbase-deployment.yml
    kubectl apply -f trailbase-service.yml
    ```
  </Step>
</Steps>

<Note>
  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.
</Note>

## Podman Support

TrailBase images also work with Podman:

```bash theme={null}
# 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:

```bash theme={null}
# 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:

<Steps>
  <Step title="Clone the repository">
    ```bash theme={null}
    git clone https://github.com/trailbaseio/trailbase.git
    cd trailbase
    git submodule update --init --recursive
    ```
  </Step>

  <Step title="Build the image">
    ```bash theme={null}
    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
  </Step>

  <Step title="Run your custom image">
    ```bash theme={null}
    docker run -d -p 4000:4000 -v ./traildepot:/app/traildepot trailbase:custom
    ```
  </Step>
</Steps>

## Health Checks

The Docker image includes a built-in health check:

```dockerfile theme={null}
HEALTHCHECK CMD curl --fail http://localhost:4000/api/healthcheck || exit 1
```

Use this in docker-compose:

```yaml docker-compose.yml theme={null}
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:

```yaml theme={null}
services:
  trail:
    image: docker.io/trailbase/trailbase:latest
    healthcheck:
      disable: true
```

## Production Recommendations

<Accordion title="Use specific image tags">
  Avoid using `:latest` in production. Pin to specific versions:

  ```yaml theme={null}
  services:
    trail:
      image: docker.io/trailbase/trailbase:v0.1.0
  ```
</Accordion>

<Accordion title="Set resource limits">
  Define CPU and memory limits:

  ```yaml theme={null}
  services:
    trail:
      image: docker.io/trailbase/trailbase:latest
      deploy:
        resources:
          limits:
            cpus: '2'
            memory: 1G
          reservations:
            cpus: '0.5'
            memory: 256M
  ```
</Accordion>

<Accordion title="Enable logging">
  Configure log rotation:

  ```yaml theme={null}
  services:
    trail:
      image: docker.io/trailbase/trailbase:latest
      logging:
        driver: "json-file"
        options:
          max-size: "10m"
          max-file: "3"
  ```
</Accordion>

<Accordion title="Use secrets for sensitive data">
  Store sensitive configuration in Docker secrets:

  ```yaml theme={null}
  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
  ```
</Accordion>

## Troubleshooting

### Permission Denied Errors

If you see "Permission denied" errors in logs:

```bash theme={null}
# 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:

```bash theme={null}
docker logs trailbase
# or
docker-compose logs trail
```

### Database Locked

SQLite databases can't be accessed by multiple containers:

```bash theme={null}
# 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

<CardGroup cols={2}>
  <Card title="Production Setup" icon="shield-check" href="/deployment/production">
    Production checklist, security hardening, and monitoring
  </Card>

  <Card title="Configuration" icon="gear" href="/deployment/configuration">
    Detailed configuration options and environment variables
  </Card>
</CardGroup>
