How to Configure Upstash for LunaTV Deployments: Complete Setup Guide

Set NEXT_PUBLIC_STORAGE_TYPE=upstash and provide UPSTASH_URL and UPSTASH_TOKEN environment variables to enable managed Redis storage for user data, favorites, and playback history in LunaTV.

LunaTV is an open-source media platform that supports multiple storage backends for persisting runtime data. Configuring Upstash provides a fully managed, serverless Redis experience ideal for cloud-native deployments where you want to avoid maintaining your own Redis infrastructure.

Prerequisites for Upstash Integration

Before deploying, ensure you have:

  • An active Upstash account with a Redis database provisioned
  • The HTTPS endpoint URL (UPSTASH_URL) and authentication token (UPSTASH_TOKEN) from your Upstash console
  • LunaTV source code that includes the Upstash storage implementation found in src/lib/upstash.db.ts

Required Environment Variables

LunaTV reads three specific environment variables at runtime to initialize the Upstash connection. According to the source code in src/lib/db.ts, the factory function checks process.env.NEXT_PUBLIC_STORAGE_TYPE and instantiates UpstashRedisStorage when the value is upstash.

Configure these variables in your deployment environment:

  1. NEXT_PUBLIC_STORAGE_TYPE – Must be set to upstash (defaults to localstorage if omitted).
  2. UPSTASH_URL – The HTTPS endpoint provided by Upstash (e.g., https://clever-duck-12345.upstash.io).
  3. UPSTASH_TOKEN – The read-write token for your Upstash Redis database.

If either UPSTASH_URL or UPSTASH_TOKEN is undefined, the constructor in src/lib/upstash.db.ts throws an error, preventing the application from starting with invalid credentials.

Docker Compose Configuration

For containerized deployments using Docker Compose, define the environment variables as follows:

services:
  lunatv:
    image: ghcr.io/moontechlab/lunatv:latest
    ports:
      - "3000:3000"
    environment:
      - NEXT_PUBLIC_STORAGE_TYPE=upstash
      - UPSTASH_URL=https://your-endpoint.upstash.io
      - UPSTASH_TOKEN=your-upstash-token
      - USERNAME=admin
      - PASSWORD=your_secure_password

How the Upstash Storage Layer Works

Understanding the implementation details in src/lib/upstash.db.ts helps optimize your deployment and troubleshoot issues.

Storage Selection Logic

The application uses a factory pattern in src/lib/db.ts to select storage backends. The middleware (src/middleware.ts) and database layer read process.env.NEXT_PUBLIC_STORAGE_TYPE at runtime. When set to upstash, the factory creates an instance of UpstashRedisStorage rather than the default local storage or standard Redis implementations.

Singleton Client Pattern

UpstashRedisStorage obtains a singleton Redis client via getUpstashRedisClient(). This function constructs the client using your UPSTASH_URL and UPSTASH_TOKEN, ensuring connection reuse across requests and preventing connection pool exhaustion in serverless environments.

Automatic Retry Mechanism

All Redis operations wrap in a withRetry() utility defined in src/lib/upstash.db.ts. This wrapper automatically retries transient connection failures (such as ECONNREFUSED) up to three times before throwing, providing resilience against temporary network interruptions.

Data Model and Hash Keys

Upstash stores each user's data in separate Hash keys following the pattern u:{user}:{type}. The storage layer manages:

  • u:{user}:pr – Playback records and watch history
  • u:{user}:fav – Favorites and bookmarks
  • u:{user}:search – Search query history

The UpstashRedisStorage class provides methods for CRUD operations on these hashes, password hashing, admin configuration, and data migration utilities.

Programmatic Usage Examples

For custom scripts or server-side operations, import the storage class directly:

import { UpstashRedisStorage } from '@/lib/upstash.db';

// The client automatically reads UPSTASH_URL and UPSTASH_TOKEN from process.env
const storage = new UpstashRedisStorage();

// Add a favorite movie for user "alice"
await storage.setFavorite('alice', 'movie:12345', {
  id: '12345',
  title: 'Inception',
  type: 'movie',
  ts: Date.now(),
});

To manually switch storage modes in code, use the factory pattern from src/lib/db.ts:

export function createStorage(): IStorage {
  const type = process.env.NEXT_PUBLIC_STORAGE_TYPE as
    | 'redis' | 'kvrocks' | 'upstash' | undefined;

  if (type === 'upstash') return new UpstashRedisStorage();
  // Additional branches for other storage types...
}

Summary

  • Set NEXT_PUBLIC_STORAGE_TYPE=upstash to activate the Upstash storage backend in LunaTV.
  • Provide UPSTASH_URL and UPSTASH_TOKEN environment variables; missing values trigger startup errors.
  • The UpstashRedisStorage class in src/lib/upstash.db.ts manages connection pooling, automatic retries, and data serialization.
  • User data persists in Upstash Redis hashes using keys formatted as u:{user}:{data_type}.
  • The middleware in src/middleware.ts reads the storage type early in the request pipeline to ensure consistent backend selection.

Frequently Asked Questions

What happens if UPSTASH_URL or UPSTASH_TOKEN is missing?

The application throws a configuration error during initialization. The constructor in src/lib/upstash.db.ts validates these environment variables and prevents startup to avoid runtime failures in production.

Can I migrate existing data from local storage to Upstash?

LunaTV includes data migration utilities within the storage layer, but you must manually export data from your current backend and import it into Upstash. The application does not automatically migrate existing local storage data to Redis on startup.

Does the Upstash integration support Redis pub/sub features?

The current implementation focuses on Hash-based persistence for user records, favorites, and playback history. While the underlying Upstash client supports pub/sub capabilities, the core UpstashRedisStorage class does not implement real-time messaging features.

How does the retry mechanism handle network failures?

All Redis operations wrap in withRetry(), which catches transient errors such as ECONNREFUSED and retries the operation up to three times. This ensures resilience against temporary network interruptions common in serverless and edge computing environments.

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 →