# How to Configure TREK with Docker Compose for Production: A Complete Security Guide

> Securely configure TREK with Docker Compose for production using this comprehensive guide. Learn to leverage read-only filesystems and dropped capabilities for robust security.

- Repository: [Maurice/TREK](https://github.com/mauriceboe/TREK)
- Tags: how-to-guide
- Published: 2026-07-03

---

**TREK provides a production-hardened [`docker-compose.yml`](https://github.com/mauriceboe/TREK/blob/main/docker-compose.yml) that enables read-only root filesystems, drops Linux capabilities, and requires only a `.env` file and two bind-mounted volumes to run securely behind a reverse proxy.**

The mauriceboe/TREK repository ships with a security-first Docker Compose configuration designed specifically for production workloads. When you configure TREK with Docker Compose for production, you leverage a hardened service definition that implements defense-in-depth principles without requiring manual security tuning.

## Security Hardening in the Production Compose File

The [`docker-compose.yml`](https://github.com/mauriceboe/TREK/blob/main/docker-compose.yml) in the repository root applies several Linux security mechanisms by default. These settings create a minimal attack surface by restricting what the containerized Node.js process can access or execute.

### Read-Only Root Filesystem

The `app` service mounts the container filesystem as **read-only** using `read_only: true`. This prevents runtime modifications to application code or system binaries. To accommodate necessary write operations, the configuration mounts a `tmpfs` volume at `/tmp` with specific security flags:

```yaml
tmpfs: /tmp:noexec,nosuid,size=64m

```

This provides a writable, in-memory temporary directory that prohibits execution (`noexec`) and set-user-ID operations (`nosuid`), as defined in [`docker-compose.yml`](https://github.com/mauriceboe/TREK/blob/main/docker-compose.yml) lines 5 and 14.

### Linux Capability Restrictions

The container drops all Linux capabilities via `cap_drop: [ALL]` and selectively re-adds only the minimum required set:

- **CHOWN**: Required to change file ownership during entrypoint initialization
- **SETUID** and **SETGID**: Required to drop privileges from root to the `node` user

Additionally, `security_opt: no-new-privileges:true` prevents the process from gaining extra privileges through setuid or setgid binaries. These settings appear in [`docker-compose.yml`](https://github.com/mauriceboe/TREK/blob/main/docker-compose.yml) lines 6, 8, and 10.

## Volume Configuration for Data Persistence

TREK requires two persistent storage locations that survive container restarts. The compose file uses bind mounts by default, mapping host directories to container paths:

- `./data` → `/app/data`: Stores the SQLite database, application logs, and generated encryption key files
- `./uploads` → `/app/uploads`: Holds user-uploaded media files

These mappings are documented in [`wiki/Install-Docker-Compose.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Install-Docker-Compose.md) line 21. If you prefer Docker-managed named volumes over bind mounts, replace the host paths with volume names:

```yaml
volumes:
  - trek_data:/app/data
  - trek_uploads:/app/uploads

```

Then declare the volumes at the compose file level:

```yaml
volumes:
  trek_data:
  trek_uploads:

```

## Required Environment Variables

All runtime configuration flows through environment variables loaded from a `.env` file placed alongside [`docker-compose.yml`](https://github.com/mauriceboe/TREK/blob/main/docker-compose.yml). The container refuses to start without specific variables defined.

### Core Configuration

At minimum, set these four variables:

| Variable | Purpose | Example |
|----------|---------|---------|
| `ENCRYPTION_KEY` | 32-byte hex string for at-rest data encryption | `openssl rand -hex 32` |
| `TZ` | Local timezone for scheduled tasks and logs | `Europe/Berlin` |
| `ALLOWED_ORIGINS` | CORS whitelist for browser requests | `https://trek.example.com` |
| `APP_URL` | Canonical URL for email links and redirects | `https://trek.example.com` |

The `ENCRYPTION_KEY` persists automatically to `data/.encryption_key` after the first start. These variables are fully documented in [`wiki/Environment-Variables.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Environment-Variables.md).

### HTTPS and Security Flags

When running behind a TLS-terminating reverse proxy, enable additional security headers:

```bash
FORCE_HTTPS=true
TRUST_PROXY=1

```

Setting `FORCE_HTTPS=true` enforces HTTPS redirects, HSTS headers, Content Security Policy upgrades, and secure cookies. The `TRUST_PROXY=1` variable tells the Express.js server to trust the first proxy hop, preventing redirect loops. You may also explicitly set `COOKIE_SECURE=true`, though this auto-enables when `FORCE_HTTPS` is active.

## Reverse Proxy and TLS Termination

The TREK compose configuration assumes TLS termination occurs at a reverse proxy (nginx, Caddy, or Traefik). The container exposes port 3000 for upstream communication. When configuring your proxy, ensure you forward the protocol and client IP information:

```nginx
location / {
    proxy_pass http://localhost:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $remote_addr;
}

```

Complete nginx and Caddy examples appear in [`wiki/Reverse-Proxy.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Reverse-Proxy.md). Remember to set `TRUST_PROXY=1` when using this configuration to prevent Express from treating the proxy as an untrusted client.

## Image Tagging Strategy

The default [`docker-compose.yml`](https://github.com/mauriceboe/TREK/blob/main/docker-compose.yml) references `mauriceboe/trek:dev` for development. For production, pin to a stable version to prevent accidental major updates:

```yaml

# Track the latest 3.x release

image: mauriceboe/trek:3

# Or pin to a specific patch version

image: mauriceboe/trek:3.0.15

```

This strategy ensures reproducible deployments and controlled upgrade paths, as detailed in [`wiki/Install-Docker-Compose.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Install-Docker-Compose.md) line 61.

## Deployment Commands

First, create the `.env` file:

```bash
cat > .env << 'EOF'
ENCRYPTION_KEY=$(openssl rand -hex 32)
TZ=Europe/Berlin
ALLOWED_ORIGINS=https://trek.example.com
APP_URL=https://trek.example.com
FORCE_HTTPS=true
TRUST_PROXY=1
EOF

```

Start the application:

```bash
docker compose up -d
docker compose logs -f

```

Upgrade to a newer version:

```bash
docker compose pull
docker compose up -d --force-recreate

```

The `restart: unless-stopped` policy in [`docker-compose.yml`](https://github.com/mauriceboe/TREK/blob/main/docker-compose.yml) line 51 ensures the container automatically restarts after host reboots or unexpected failures.

## Summary

- **TREK's [`docker-compose.yml`](https://github.com/mauriceboe/TREK/blob/main/docker-compose.yml)** implements production security defaults including read-only filesystems, dropped capabilities, and no-new-privileges restrictions.
- **Two volumes** (`./data` and `./uploads`) persist SQLite databases and user media across container restarts, configurable as bind mounts or named volumes.
- **Four environment variables** (`ENCRYPTION_KEY`, `TZ`, `ALLOWED_ORIGINS`, `APP_URL`) are required to start, with `FORCE_HTTPS` and `TRUST_PROXY` necessary for reverse proxy deployments.
- **Reverse proxy configuration** must include `X-Forwarded-Proto` headers and set `TRUST_PROXY=1` to avoid redirect loops.
- **Pin image tags** to major versions (e.g., `mauriceboe/trek:3`) rather than using `latest` or `dev` tags in production.

## Frequently Asked Questions

### Does TREK require root privileges to run in Docker?

No. While the container starts as root to perform initial permission setup, the entrypoint script drops privileges to the `node` user before starting the application. The `cap_add` settings in [`docker-compose.yml`](https://github.com/mauriceboe/TREK/blob/main/docker-compose.yml) provide only the specific capabilities needed for this privilege drop (CHOWN, SETUID, SETGID), and `no-new-privileges:true` prevents privilege escalation.

### Can I use PostgreSQL or MySQL instead of SQLite?

The provided [`docker-compose.yml`](https://github.com/mauriceboe/TREK/blob/main/docker-compose.yml) configures TREK for SQLite via the `./data` volume mount. To use PostgreSQL or MySQL, you would need to modify the environment variables to include database connection strings (see [`wiki/Environment-Variables.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Environment-Variables.md)) and add a database service to your compose file, though the official production configuration optimizes for the zero-config SQLite setup.

### How do I backup TREK data when using Docker Compose?

Backup the `./data` directory, which contains the SQLite database (`trek.db`), application logs, and the `.encryption_key` file. If using named volumes instead of bind mounts, use `docker run --rm -v trek_data:/data -v $(pwd):/backup alpine tar czf /backup/trek-backup.tar.gz -C /data .` to create a compressed archive.

### What happens if I don't set FORCE_HTTPS behind a reverse proxy?

Without `FORCE_HTTPS`, TREK will not redirect HTTP traffic to HTTPS, won't set the Secure flag on cookies, and won't send HSTS headers. However, if your reverse proxy terminates TLS but doesn't properly set `X-Forwarded-Proto`, leaving `FORCE_HTTPS` disabled prevents infinite redirect loops while you configure the proxy headers correctly.