Skip to content

Technitium DNS Companion - Getting Started Guide

Getting started is quick and easy with Docker (or an equivalent container runtime). Just a few environment variables are needed to connect to your Technitium DNS nodes.

Getting Started (2-minute path)

You don't need to clone the repo. Download the script and run it from the host where you want to run the container:

  • macOS/Linux:
curl -fsSL https://raw.githubusercontent.com/Fail-Safe/Technitium-DNS-Companion/main/scripts/docker-quickstart.sh -o docker-quickstart.sh
chmod +x docker-quickstart.sh
./docker-quickstart.sh
  • Windows PowerShell:
iwr https://raw.githubusercontent.com/Fail-Safe/Technitium-DNS-Companion/main/scripts/docker-quickstart.ps1 -OutFile docker-quickstart.ps1
powershell -ExecutionPolicy Bypass -File .\docker-quickstart.ps1
  • What the script does:
  • Verifies Docker is running
  • Downloads .env.example into technitium.env if missing
  • Pulls the selected image first (so :latest stays current)
  • Shows the exact docker run command and can run it for you
  • Prompts to confirm HTTP/HTTPS ports before running (must be different; it will re-prompt if you choose the same port)

  • If technitium.env didn’t exist, the script will create it and exit so you can edit it.

  • After editing technitium.env, rerun the script and press Enter to launch (any other key cancels).

Option B: Manual docker run (no clone required)

Runs the single container that serves both the API and built frontend. Use this if you prefer to type the command yourself.

  1. Prepare environment:

  2. Download .env.example (or copy it from a clone) and save as technitium.env.

  3. Minimum variables:
  4. TECHNITIUM_NODES=node1,node2
  5. TECHNITIUM_<NODE>_BASE_URL for each node.
  6. Required for interactive UI access (v1.4+): Technitium login/RBAC (session auth).
  7. Recommended for background features in session-auth mode: TECHNITIUM_BACKGROUND_TOKEN (least-privilege, read-only).
  8. Legacy only (Technitium DNS < v14 / migration): env-token mode using TECHNITIUM_<NODE>_TOKEN.
  9. Optional: set TZ, CORS_ORIGINS, and HTTPS variables.

  10. Run the container:

docker run --rm -p 3000:3000 -p 3443:3443 \
  --env-file technitium.env \
  -v technitium-dns-companion-data:/data \
  ghcr.io/fail-safe/technitium-dns-companion:latest

Notes on persistence (/data volume):

  • -v technitium-dns-companion-data:/data is recommended. It is used for caches and optional on-disk features.
  • If you enable the optional SQLite rolling query-log store, set QUERY_LOG_SQLITE_PATH to a path under /data so stored logs survive container restarts.
  • DNS Filtering History snapshots can also be stored under /data (see .env.example / DNS_FILTERING_SNAPSHOT_DIR).

Optional: Enable SQLite rolling query logs storage (accuracy for “Last 24h” presets)

  • In technitium.env:
QUERY_LOG_SQLITE_ENABLED=true
QUERY_LOG_SQLITE_PATH=/data/query-logs.sqlite
QUERY_LOG_SQLITE_RETENTION_HOURS=24
QUERY_LOG_SQLITE_POLL_INTERVAL_MS=10000
QUERY_LOG_SQLITE_OVERLAP_SECONDS=60
QUERY_LOG_SQLITE_MAX_ENTRIES_PER_POLL=20000
  • With session auth (v1.4+: always enabled for interactive UI), ingestion runs as a background task and requires TECHNITIUM_BACKGROUND_TOKEN (least-privilege token that can read query logs). Without it, the DB may exist but will not stay up to date.

  • Direct access defaults to automatically generated self-signed HTTPS: https://localhost:3443

  • HTTP on port 3000 is intended for a trusted host-based TLS reverse proxy (TRUST_PROXY=true).

  • Use a trusted HTTPS certificate (optional):

  • Place certs locally:

certs/
├── fullchain.pem
└── privkey.pem
  • In technitium.env, set:
HTTPS_ENABLED=true
HTTPS_PORT=3443
HTTPS_CERT_PATH=/app/certs/fullchain.pem
HTTPS_KEY_PATH=/app/certs/privkey.pem
# HTTPS_CA_PATH=/app/certs/chain.pem  # (optional) Specify if you have a separate CA file
  • Mount certs when running:
docker run --rm -p 3000:3000 -p 3443:3443 \
  --env-file technitium.env \
  -v technitium-dns-companion-data:/data \
  -v $(pwd)/certs:/app/certs:ro \
  ghcr.io/fail-safe/technitium-dns-companion:latest
  • For Windows PowerShell, use ${PWD}\certs for the host path.

  • Operations:

  • Logs: docker logs -f <container-id> (if run without --rm, add --name tdc then docker logs -f tdc)

  • Restart (named container): docker restart tdc
  • Upgrade image: docker pull ghcr.io/fail-safe/technitium-dns-companion:latest

Option C: docker compose (from a repo clone)

Use this if you’ve cloned the repo and prefer compose for repeatability or dev overrides. The included file requires Docker Compose 2.24 or later for optional environment-file overlays.

  1. Copy .env.example to .env and set your variables. .env.example is the complete, commented configuration reference, so the available settings are documented locally without needing this guide open. Compose loads it first for backward-compatible defaults, then overlays values from .env. The user file is optional to Compose so existing deployments that configure the service another way continue to start.

  2. Start the published image. Compose creates the persistent data directory if it does not already exist:

docker compose up -d

To build the checked-out source instead, use:

docker compose -f docker-compose.yml -f docker-compose.local-build.yml up -d --build
  1. HTTPS: the default is persistent self-signed HTTPS on port 3443. For a trusted certificate, place certs in certs/, set the HTTPS variables in .env, and uncomment the certificate mount in docker-compose.yml. For a host-based TLS reverse proxy, set TRUST_PROXY=true. Port 3000 retains its existing all-interface bind by default; set COMPANION_HTTP_BIND=127.0.0.1 when only a host-based reverse proxy should reach it.

  2. Operations:

  3. Logs: docker compose logs -f

  4. Restart: docker compose restart
  5. Upgrade: run git pull, then docker compose up -d

Compose also limits the service's local json-file logs to three 10 MB files by default. Use COMPANION_LOG_MAX_SIZE and COMPANION_LOG_MAX_FILES in .env to tune those limits.

Opt into the beta channel

Set the image override in .env:

COMPANION_IMAGE=ghcr.io/fail-safe/technitium-dns-companion:beta

Then pull and recreate the service:

docker compose pull
docker compose up -d

The beta and next tags move with the next branch. Each published build also has an immutable sha-<commit> tag. Record the revision shown in the About dialog or inspect the image labels before reporting a problem:

docker image inspect "$COMPANION_IMAGE" \
  --format '{{ index .Config.Labels "org.opencontainers.image.revision" }}'

To return to stable, change COMPANION_IMAGE back to ghcr.io/fail-safe/technitium-dns-companion:latest, then run the same pull and up commands. See the Beta Testing Guide for exact-build pinning and rollback guidance.

Troubleshooting

  • Container will not start: docker compose logs then docker compose config to validate env vars.
  • Port bind error ("port is already allocated"): pick different host ports (e.g., -p 1234:3000 -p 1235:3443) or stop the other container/service using that port.
  • Cannot connect to Technitium DNS nodes: Check URLs and tokens in .env.

Health checks

The image health check probes https://127.0.0.1:<HTTPS_PORT>/api/health and http://127.0.0.1:<PORT>/api/health from inside the container. Compose inherits this image-level check.

  • HTTPS is attempted first, then HTTP as fallback.
  • The probe uses Node.js and does not require wget or curl in the image.

Quick verification:

docker compose ps
docker inspect --format='{{json .State.Health}}' "$(docker compose ps -q technitium-dns-companion)" | jq

Post-upgrade permissions check (v1.4+)

Recent images run as a non-root user (node, uid/gid 1000). If older bind mounts or volumes contain root-owned files, startup or background tasks may fail with permission errors such as:

  • EACCES: permission denied, open /app/certs/... (HTTPS certs not readable)
  • attempt to write a readonly database (SQLite query logs DB not writable)

Quick check inside the container:

docker exec -it <container> sh -lc 'id; ls -ld /app/certs /data /app/config; ls -l /app/certs 2>/dev/null || true; ls -l /data 2>/dev/null || true'

Recommended permissions:

  • Certificate directories should be traversable/readable (commonly 755)
  • Certificate/key files should be readable by the container user (commonly 644)
  • SQLite DB path and related files (.sqlite, -wal, -shm) should be writable by uid 1000

Example remediation (adjust to your host paths/volume contents):

# Certs (host or volume content)
chmod 755 /path/to/certs /path/to/certs/certs
chmod 644 /path/to/certs/certs/*

# App state volume (from container as root, then restart)
docker exec -u 0 <container> sh -lc 'chown -R node:node /data'
docker restart <container>

Temporary workaround only: run the container as root while fixing file ownership, then switch back to non-root.

Docker Secrets / File-based Secrets

For production deployments, you can use Docker secrets or file-based secrets instead of passing sensitive values directly as environment variables. The application supports the common _FILE suffix pattern.

Supported Variables

Environment Variable File Variant
TECHNITIUM_BACKGROUND_TOKEN TECHNITIUM_BACKGROUND_TOKEN_FILE
TECHNITIUM_<NODE>_TOKEN TECHNITIUM_<NODE>_TOKEN_FILE

When the _FILE variant is set, the application reads the secret from that file path. The _FILE variant takes precedence if both are set.

Docker Swarm Example

  1. Create the secrets:
echo "your-background-token" | docker secret create technitium_background_token -
  1. Reference in your stack file:
version: "3.8"

services:
  technitium-dns-companion:
    image: ghcr.io/fail-safe/technitium-dns-companion:latest
    environment:
      TECHNITIUM_NODES: "node1,node2"
      TECHNITIUM_NODE1_BASE_URL: "http://dns1.example.com:5380"
      TECHNITIUM_NODE2_BASE_URL: "http://dns2.example.com:5380"
      # Use _FILE variants for secrets
      TECHNITIUM_BACKGROUND_TOKEN_FILE: /run/secrets/technitium_background_token
    secrets:
      - technitium_background_token
    ports:
      - "3000:3000"
    volumes:
      - companion-data:/data

secrets:
  technitium_background_token:
    external: true

volumes:
  companion-data:

Kubernetes Example

  1. Create the secret:
kubectl create secret generic technitium-tokens \
  --from-literal=background-token='your-background-token'
  1. Mount in your deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: technitium-dns-companion
spec:
  template:
    spec:
      containers:
        - name: companion
          image: ghcr.io/fail-safe/technitium-dns-companion:latest
          env:
            - name: TECHNITIUM_NODES
              value: "node1,node2"
            - name: TECHNITIUM_BACKGROUND_TOKEN_FILE
              value: /secrets/background-token
          volumeMounts:
            - name: secrets
              mountPath: /secrets
              readOnly: true
      volumes:
        - name: secrets
          secret:
            secretName: technitium-tokens

Per-Node Tokens

For legacy deployments with per-node tokens:

# Create secrets for each node
echo "node1-token" | docker secret create technitium_node1_token -
echo "node2-token" | docker secret create technitium_node2_token -
environment:
  TECHNITIUM_NODE1_TOKEN_FILE: /run/secrets/technitium_node1_token
  TECHNITIUM_NODE2_TOKEN_FILE: /run/secrets/technitium_node2_token
secrets:
  - technitium_node1_token
  - technitium_node2_token

Need contributor instructions? See DEVELOPMENT.md.