1 Deployment
xpltd_admin edited this page 2026-04-03 22:42:10 -06:00

Deployment

Meta Value
Repo xpltdco/tubearr
Page Deployment
Audience developers, agents, newcomers
Last Updated 2026-04-04
Status current

Docker Image

Tubearr uses a multi-stage Docker build to produce a minimal production image.

Build Stages

Stage Base Purpose
deps node:22-alpine Install all npm dependencies
build inherits deps Compile TypeScript backend + build React SPA
runtime node:22-alpine Production image with yt-dlp, ffmpeg, compiled app

Runtime Image Contents

The final image includes:

  • Compiled JavaScript (dist/)
  • Built React SPA (dist/frontend/)
  • Drizzle migration files (drizzle/)
  • Production npm dependencies only
  • System packages: Python 3, pip, yt-dlp, ffmpeg
  • tsx — for ESM module loading at runtime
  • Directories: /config (database, cookies, settings), /media (downloaded files)

Build the Image

docker build -t tubearr:latest .

Docker Compose (Production)

File: docker-compose.yml

services:
  tubearr:
    build: .
    container_name: tubearr
    ports:
      - "8989:8989"
    volumes:
      - tubearr-config:/config
      - ./media:/media
    environment:
      - NODE_ENV=production
      - TUBEARR_PORT=8989
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8989/ping"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 15s
    restart: unless-stopped

volumes:
  tubearr-config:

xpltd Infrastructure Deployment

On the xpltd infrastructure, Tubearr runs as part of the Docker Compose stack on ub01 (10.0.0.10):

  • Compose project: Located in /vmPool/r/compose/ on ub01
  • Container name: tubearr
  • Internal port: 8989
  • Media storage: Bind-mounted to a path on the ub01 filesystem

Port Mappings

Port Protocol Purpose
8989 HTTP Fastify server (API + SPA + WebSocket)

Only one port is needed — the Fastify server handles everything (REST API, static frontend files, WebSocket upgrade).

Volume Mounts

Mount Container Path Purpose Backup Priority
tubearr-config (named volume) /config SQLite database (tubearr.db), cookie files, settings High — contains all application state
Bind mount /media Downloaded media files Medium — re-downloadable but large

Config Volume Contents

/config/
├── tubearr.db          # SQLite database (all channels, content, queue, settings)
├── tubearr.db-wal      # WAL journal (auto-managed by SQLite)
├── tubearr.db-shm      # Shared memory file (auto-managed by SQLite)
└── cookies/            # Per-platform cookie files for authenticated downloads
    ├── youtube.txt
    └── soundcloud.txt

Nginx / Reverse Proxy

Tubearr is exposed externally via nginx01 (10.0.0.9) which handles TLS termination.

Traffic Flow

Client → Cloudflare (DNS: tubearr.xpltd.co)
  → nginx01:443 (TLS termination)
  → ub01:8989 (Tubearr container)

Nginx Configuration

The nginx vhost for Tubearr proxies all requests including WebSocket upgrades:

server {
    listen 443 ssl;
    server_name tubearr.xpltd.co;

    # TLS certs managed by nginx01
    ssl_certificate     /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://10.0.0.10:8989;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

DNS Configuration

Layer Record Value
Cloudflare (external) tubearr.xpltd.co CNAME dyndns.xpltd.co
AdGuard Home (internal) *.xpltd.co rewrite 10.0.0.9 (nginx01)

Health Checks

Docker Health Check

The compose file includes a health check that polls the /ping endpoint:

healthcheck:
  test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8989/ping"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 15s
  • Start period: 15 seconds grace period for startup (migrations, yt-dlp update check)
  • Interval: Check every 30 seconds
  • Retries: Mark unhealthy after 3 consecutive failures

Application Health Endpoint

GET /api/v1/health returns component-level status:

  • Database connectivity
  • yt-dlp availability and version
  • Scheduler state
  • Disk space

Liveness Probe

GET /ping — Lightweight, no auth required. Returns 200 if the server is accepting requests.

Monitoring & Logging

Logging

Tubearr uses Fastify's built-in Pino logger:

  • Format: JSON (structured) in production, pretty-printed in development
  • Level: Controlled by TUBEARR_LOG_LEVEL (default: info)
  • Output: stdout/stderr — captured by Docker logging driver

View logs:

docker logs -f tubearr

Key Log Events

Level Event
info Server started, channel scanned, download completed
warn Rate limited, retry queued, yt-dlp update available
error Download failed, database error, notification dispatch failure

Backup Considerations

What to Back Up

Data Location Strategy
SQLite database /config/tubearr.db Copy while using .backup or with WAL checkpoint. Most critical.
Cookie files /config/cookies/ Simple file copy. Contains platform auth cookies.
Media files /media/ Optional — large, can be re-downloaded. Use rsync for incremental.

SQLite Backup

For a consistent backup of a WAL-mode database:

# From host, exec into container
docker exec tubearr sqlite3 /config/tubearr.db ".backup /config/tubearr-backup.db"

# Then copy the backup file out
docker cp tubearr:/config/tubearr-backup.db ./tubearr-backup.db

Recovery

  1. Stop the container: docker compose down
  2. Replace /config/tubearr.db with backup
  3. Remove WAL/SHM files (they'll be recreated): rm -f /config/tubearr.db-wal /config/tubearr.db-shm
  4. Start the container: docker compose up -d