Skip to content

Self-Hosting Guide

Deploy MediaLocker on your own infrastructure with Docker Compose. This guide covers everything from requirements to production scaling.

MediaLocker is a hybrid deployment: you self-host the application services, Redis, Caddy, and analytics with Docker Compose, while Postgres and Auth run on Supabase Cloud (managed) and object storage lives in Hetzner Object Storage. There is no database, GoTrue, or PostgREST container to operate — the stack connects to your Supabase project over the network.

Architecture Overview

                        ┌──────────────────────────────┐
                        │     Caddy (Reverse Proxy)     │
                        │     Wildcard TLS (DNS-01)     │
                        └──────────┬───────────────────┘

                  ┌────────────────┼────────────────┐
                  ▼                ▼                ▼
                app.*            api.*            mcp.*
               (app)            (api)            (mcp)
                  │                │                │
                  └────────────────┼────────────────┘


                                 redis  ◄── self-hosted (Docker)

    Postgres + Auth are Supabase Cloud (managed) — the api/worker/mcp
    services connect over DATABASE_URL + the Supabase Auth API.

    Object storage is external (Hetzner Object Storage);
    the api/worker/mcp services hold the master credential
    and issue presigned URLs — no local storage container.

Components

ComponentWhereDescription
CaddySelf-hostedReverse proxy, wildcard TLS via DNS-01
appSelf-hostedNext.js dashboard (app.medialocker.io)
apiSelf-hostedBackend API (api.medialocker.io)
mcpSelf-hostedMCP server (mcp.medialocker.io)
workerSelf-hostedBackground job processor (derivatives, runs in-region)
redisSelf-hostedRedis with AOF persistence (port 6379, internal)
plausibleSelf-hostedPrivacy-first analytics (+ ClickHouse)
docsSelf-hostedVitePress documentation site
PostgresSupabase CloudManaged Postgres — connect via DATABASE_URL
AuthSupabase CloudManaged GoTrue — Supabase Auth API
Object storageHetznerExternal Hetzner Object Storage — no local container; the backend holds the master credential and issues presigned URLs

Subdomains

SubdomainService
app.medialocker.ioDashboard
api.medialocker.ioREST API
mcp.medialocker.ioMCP server
docs.medialocker.ioDocumentation
plausible.medialocker.ioAnalytics dashboard

Quick Start

bash
# Clone the repository
git clone https://github.com/medialocker/medialocker-app.git
cd medialocker-app

# Copy environment template
cp .env.example .env
# Edit .env with your values — including the Supabase Cloud
# connection strings and keys (see Docker Compose Setup)

# Run migrations against the Supabase Cloud DB
# (use the SESSION pooler / port 5432 or a direct connection)
DATABASE_URL=<session-or-direct-url> pnpm --filter @medialocker/db migrate

# Setup Stripe (also needs a session/direct connection)
DATABASE_URL=<session-or-direct-url> pnpm stripe:setup

# Start the self-hosted services
docker compose -f infra/docker-compose.yml up -d

Prerequisites

See the Requirements page for detailed system requirements before starting. You will need a Supabase Cloud project in addition to your host.

Production Readiness Checklist

Before going to production, verify each item:

  • [ ] Supabase Cloud project provisioned, migrations applied, and Auth configured (see Docker Compose Setup)
  • [ ] All required environment variables configured (see Environment Variables)
  • [ ] Wildcard TLS certificates obtained and auto-renewal confirmed (see Wildcard TLS)
  • [ ] Supabase Cloud backups/PITR retention confirmed and Redis backups scheduled (see Backups)
  • [ ] Hetzner Object Storage project access verified (master credential set, durability relied on)
  • [ ] Resource limits set on all Docker containers
  • [ ] Firewall rules applied — only ports 80 and 443 exposed
  • [ ] Health checks passing for all services: docker compose -f infra/docker-compose.yml ps
  • [ ] Log aggregation configured (see Scaling)
  • [ ] Upgrade procedure tested on staging (see Upgrade Guide)
  • [ ] Monitoring and alerting configured (Prometheus + Grafana recommended)
  • [ ] Stripe integration verified with test mode before switching to live keys

Released under the AGPL-3.0 License.