How to Deploy LunaTV Using Docker: Multi-Stage Builds and Production Configuration

Deploy LunaTV using Docker by pulling the pre-built GHCR image or building the multi-stage Dockerfile locally, setting required authentication and storage environment variables, and exposing port 3000 for the Next.js application.

The MoonTechLab/LunaTV repository packages this streaming platform as an optimized, production-ready container. Understanding how to deploy LunaTV using Docker ensures consistent deployments across Docker Desktop, Kubernetes, and cloud-native platforms.

Anatomy of the LunaTV Multi-Stage Dockerfile

The production image defined in Dockerfile at the repository root employs a three-stage build strategy to minimize attack surface and image size while maximizing build cache efficiency.

Stage 1: Dependency Installation (deps)

The deps stage copies only package.json and the pnpm lockfile to leverage Docker layer caching. It installs all Node.js dependencies—including devDependencies—using pnpm, creating a reusable base layer for subsequent builds.

Stage 2: Application Build (builder)

The builder stage copies the installed node_modules from deps along with the full source tree. It sets DOCKER_ENV=true and executes pnpm run build to generate a standalone Next.js output in .next/standalone. This stage compiles the TypeScript source and prepares optimized production assets.

Stage 3: Production Runtime (runner)

The final runner stage starts from a clean Alpine Linux image, creates a non-root nextjs user for security, and copies only the essential artifacts: the standalone build, public assets, and the custom start.js entry point. The container exposes port 3000 and launches the application via node start.js.

Required Environment Variables

LunaTV requires specific environment variables at runtime for authentication and storage configuration. These are read by src/lib/config.ts and consumed by the custom start.js initialization script.

  • USERNAME / PASSWORD: Administrator credentials (mandatory).
  • NEXT_PUBLIC_STORAGE_TYPE: Backend for playback records—set to redis, kvrocks, or upstash.
  • Connection URLs: Provide REDIS_URL, KVROCKS_URL, or UPSTASH_URL/UPSTASH_TOKEN based on your storage selection.
  • Optional Branding: Configure SITE_BASE, NEXT_PUBLIC_SITE_NAME, and ANNOUNCEMENT for site customization.

Deployment Methods

Choose between ephemeral single-container testing or persistent orchestrated deployments.

Single-Container Deployment with Docker Run

For rapid testing against an existing Redis instance, run the pre-built image from GitHub Container Registry:

docker run -d \
  --name lunatv \
  -p 3000:3000 \
  -e USERNAME=admin \
  -e PASSWORD=SuperSecretPassword \
  -e NEXT_PUBLIC_STORAGE_TYPE=redis \
  -e REDIS_URL=redis://host.docker.internal:6379 \
  ghcr.io/moontechlab/lunatv:latest

This command binds port 3000 to localhost and configures Redis as the storage backend.

Local Development with Docker Compose

For local development with persistent storage, use the example docker-compose.dev.yml. This orchestrates both the LunaTV application and a Redis service with health checks:

version: '3.8'

services:
  redis:
    image: redis:7-alpine
    container_name: lunatv-redis
    volumes:
      - redis-data:/data
    command: redis-server --appendonly yes
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
      interval: 5s
      timeout: 3s
      retries: 5

  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: lunatv-app
    ports:
      - '3000:3000'
    depends_on:
      redis:
        condition: service_healthy
    environment:
      - NEXT_PUBLIC_STORAGE_TYPE=redis
      - REDIS_URL=redis://redis:6379
      - USERNAME=admin
      - PASSWORD=admin123
      - NEXT_PUBLIC_SITE_NAME=MoonTV

volumes:
  redis-data:

The app service builds from the local Dockerfile while the redis service provides durable storage with volume persistence.

Production Deployment with Kvrocks

For production workloads requiring higher throughput, replace Redis with Apache Kvrocks. Update your Compose file to define the Kvrocks service and adjust environment variables:

services:
  kvrocks:
    image: apache/kvrocks
    container_name: moontv-kvrocks
    restart: unless-stopped
    volumes:
      - kvrocks-data:/var/lib/kvrocks

  app:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - NEXT_PUBLIC_STORAGE_TYPE=kvrocks
      - KVROCKS_URL=redis://kvrocks:6666
      - USERNAME=admin
      - PASSWORD=securepassword

Set NEXT_PUBLIC_STORAGE_TYPE=kvrocks and provide KVROCKS_URL to connect to the Kvrocks instance on its default port 6666.

Key Implementation Files

Understanding these critical files helps troubleshoot deployment issues:

  • Dockerfile: Defines the multi-stage build pipeline producing the production image.
  • docker-compose.dev.yml: Provides the reference implementation for local Redis-backed deployments.
  • start.js: Custom entry point executed by the runner stage; pre-loads configuration before initializing the Next.js server.
  • src/lib/config.ts: Centralized configuration loader that validates and exposes environment variables to the application.

Summary

  • Deploy LunaTV using Docker by leveraging the multi-stage Dockerfile that separates dependency installation, application building, and production runtime into discrete layers.
  • The runner stage executes as the non-root nextjs user on Alpine Linux, listening on port 3000.
  • Required environment variables include authentication credentials (USERNAME, PASSWORD) and storage configuration (NEXT_PUBLIC_STORAGE_TYPE plus connection URLs).
  • Choose Redis for local development via docker-compose.dev.yml, or Kvrocks for production deployments requiring persistent, high-performance storage.
  • The container is self-contained and compatible with Docker Desktop, Kubernetes, and platform-as-a-service providers like Zeabur.

Frequently Asked Questions

What storage backends does LunaTV support?

LunaTV supports three storage backends for playback records: Redis (ideal for development), Kvrocks (recommended for production), and Upstash (serverless Redis). Configure the backend using NEXT_PUBLIC_STORAGE_TYPE and provide the corresponding connection URL.

Why does the Dockerfile use a multi-stage build?

The multi-stage build minimizes the final image size and attack surface by discarding build dependencies and source code. Only the compiled standalone Next.js output from .next/standalone and the start.js script are copied into the production runner stage based on Alpine Linux.

How do I persist data when deploying LunaTV?

Data persistence depends on your storage backend. When using Docker Compose, mount named volumes (e.g., redis-data or kvrocks-data) to the respective storage containers. For Redis, use the --appendonly yes flag to enable AOF persistence. The LunaTV container itself is stateless.

Can I run LunaTV as a non-root user?

Yes, the production image explicitly creates and runs as the nextjs user within the runner stage. This security hardening ensures the application process lacks privileges to modify system files inside the container, following container security best practices.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →