# How LunaTV Manages Environment Variables: Runtime Configuration Architecture

> Discover how LunaTV manages environment variables, separating client and server secrets and caching configurations via getConfig() for efficient runtime configuration architecture.

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

---

**LunaTV centralizes all application configuration through environment variables read via `process.env`, separating client-exposed settings (prefixed with `NEXT_PUBLIC_`) from server-only secrets, and caches resolved configurations in memory through the `getConfig()` routine.**

LunaTV, an open-source media platform developed by MoonTechLab, relies entirely on environment variables for runtime configuration. The codebase implements a centralized configuration pattern that reads variables at startup, applies sensible defaults, and caches the result to optimize performance. This architecture ensures sensitive credentials remain server-side while allowing UI settings to reach the client bundle safely.

## Centralized Configuration Through [`src/lib/config.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/config.ts)

The heart of LunaTV's environment variable management resides in [`src/lib/config.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/config.ts). The `getConfig()` function constructs an `AdminConfig` object by reading `process.env` values and falling back to hardcoded defaults when variables are absent.

The implementation uses a `cachedConfig` variable to store the resolved configuration after the first call, preventing repeated environment lookups on subsequent requests. According to the MoonTechLab/LunaTV source code, this lazy-loading pattern appears at lines 94-106 of [`src/lib/config.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/config.ts).

```typescript
// src/lib/config.ts – site configuration defaults
export async function getConfig(): Promise<AdminConfig> {
  const adminConfig: AdminConfig = {
    SiteConfig: {
      SiteName: process.env.NEXT_PUBLIC_SITE_NAME || 'MoonTV',
      Announcement: process.env.ANNOUNCEMENT || '…',
      SearchDownstreamMaxPage: Number(process.env.NEXT_PUBLIC_SEARCH_MAX_PAGE) || 5,
      // …
    },
    // …
  };
  // …
}

```

Within the `SiteConfig` object, specific fields like `SiteName` default to `'MoonTV'` when `NEXT_PUBLIC_SITE_NAME` is undefined, while `SearchDownstreamMaxPage` converts the `NEXT_PUBLIC_SEARCH_MAX_PAGE` string to a number with a fallback of 5.

## Authentication and Storage Controls in Middleware

The [`src/middleware.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/middleware.ts) file handles request authentication by evaluating `process.env.PASSWORD` and `process.env.NEXT_PUBLIC_STORAGE_TYPE`. When `PASSWORD` is unset, the middleware redirects all requests to a warning page.

```typescript
// src/middleware.ts – auth & storage handling
export async function middleware(request: NextRequest) {
  const storageType = process.env.NEXT_PUBLIC_STORAGE_TYPE || 'localstorage';
  if (!process.env.PASSWORD) {
    return NextResponse.redirect(new URL('/warning', request.url));
  }
  // localstorage mode validates password directly
  if (storageType === 'localstorage') {
    if (authInfo.password !== process.env.PASSWORD) {
      return handleAuthFailure(request, pathname);
    }
    return NextResponse.next();
  }
  // other modes validate signed tokens using the PASSWORD secret
}

```

For `localstorage` mode, the middleware validates credentials directly against `process.env.PASSWORD`. For other storage backends, the same variable serves as the signing secret for token validation. The `NEXT_PUBLIC_STORAGE_TYPE` variable determines which authentication flow executes, demonstrating how LunaTV uses environment variables to alter runtime behavior.

## Third-Party Service Connection Strings

External database and API connections rely on environment variables defined in specific database adapter files. The [`src/lib/redis.db.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/redis.db.ts) module instantiates a Redis client using `process.env.REDIS_URL`, while [`src/lib/upstash.db.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/upstash.db.ts) reads `UPSTASH_URL` and `UPSTASH_TOKEN` for serverless Redis connectivity.

```typescript
// src/lib/redis.db.ts – connecting to Redis via env var
export const redis = new Redis({
  url: process.env.REDIS_URL!,
});

```

Similarly, [`src/lib/kvrocks.db.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/kvrocks.db.ts) consumes `KVROCKS_URL`, and Douban proxy configuration uses `NEXT_PUBLIC_DOUBAN_PROXY`. These variables remain server-only unless explicitly prefixed for client exposure.

## Client-Side vs Server-Side Variable Separation

LunaTV follows Next.js conventions by exposing environment variables to the browser only when prefixed with `NEXT_PUBLIC_`. Variables like `NEXT_PUBLIC_SITE_NAME`, `NEXT_PUBLIC_STORAGE_TYPE`, and `NEXT_PUBLIC_DOUBAN_PROXY` ship to the client bundle, while `PASSWORD`, `REDIS_URL`, and `USERNAME` remain server-side.

The `USERNAME` environment variable deserves special mention. It defines the owner identity injected into the initial admin user list and powers permission checks throughout the application, including the `configSelfCheck` function in [`src/lib/config.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/config.ts) (lines 36-44).

## Summary

- LunaTV reads all configuration via `process.env` in [`src/lib/config.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/config.ts)
- The `getConfig()` routine caches resolved settings in `cachedConfig` to optimize performance
- `NEXT_PUBLIC_` prefix determines client-side exposure versus server-only access
- Authentication logic in [`src/middleware.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/middleware.ts) depends on `PASSWORD` and `NEXT_PUBLIC_STORAGE_TYPE`
- Database adapters in `src/lib/*.db.ts` files consume service-specific connection URLs

## Frequently Asked Questions

### How does LunaTV handle missing environment variables?

When an environment variable is undefined, LunaTV applies hardcoded defaults within the `getConfig()` function. For example, `process.env.NEXT_PUBLIC_SITE_NAME` falls back to `'MoonTV'`, and `NEXT_PUBLIC_SEARCH_MAX_PAGE` defaults to 5.

### What distinguishes NEXT_PUBLIC_ variables from server-only variables in LunaTV?

Variables prefixed with `NEXT_PUBLIC_` are embedded into the client-side JavaScript bundle at build time, making them accessible in browser code. Server-only variables like `PASSWORD` and `REDIS_URL` never reach the client and remain accessible only in Node.js runtime contexts such as API routes and middleware.

### How does LunaTV optimize environment variable reads?

The `getConfig()` function in [`src/lib/config.ts`](https://github.com/MoonTechLab/LunaTV/blob/main/src/lib/config.ts) implements a lazy-caching mechanism using a `cachedConfig` variable. After the first invocation resolves all environment variables and defaults, subsequent calls return the cached object without re-reading `process.env`.

### Which environment variables are required for LunaTV authentication?

The `PASSWORD` environment variable is mandatory; if absent, the middleware redirects all requests to a warning page. The `NEXT_PUBLIC_STORAGE_TYPE` variable is also critical as it determines whether the application uses `localstorage`, Upstash, Redis, or KVROCKS backends.