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

> Deploy LunaTV using Docker with multi-stage builds and production configuration. Learn to pull the image or build locally, set variables, and expose port 3000 for a seamless Nextjs deployment.

- Repository: [MoonTechLab/LunaTV](https://github.com/MoonTechLab/LunaTV)
- Tags: how-to-guide
- Published: 2026-09-08

---

**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`](https://github.com/MoonTechLab/LunaTV/blob/main/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`](https://github.com/MoonTechLab/LunaTV/blob/main/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`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/config.ts) and consumed by the custom [`start.js`](https://github.com/MoonTechLab/LunaTV/blob/main/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:

```bash
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`](https://github.com/MoonTechLab/LunaTV/blob/main/docker-compose.dev.yml). This orchestrates both the LunaTV application and a Redis service with health checks:

```yaml
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:

```yaml
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`](https://github.com/MoonTechLab/LunaTV/blob/main/docker-compose.dev.yml)**: Provides the reference implementation for local Redis-backed deployments.
- **[`start.js`](https://github.com/MoonTechLab/LunaTV/blob/main/start.js)**: Custom entry point executed by the `runner` stage; pre-loads configuration before initializing the Next.js server.
- **[`src/lib/config.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/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`](https://github.com/MoonTechLab/LunaTV/blob/main/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`](https://github.com/MoonTechLab/LunaTV/blob/main/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.