---
title: "Deployment"
description: "Deployment overview for Ring Platform — production, preview, and self-hosted targets."
locale: "en"
---
# Deployment Guide

Ring Platform supports three deployment targets: **k3s cluster** (production), **Vercel** (preview), and **Docker** (self-hosted). The platform uses a unified environment configuration; only `DB_BACKEND_MODE` and `RING_DEPLOY_TARGET` change between targets.

> **Info**
> This page is a deployment strategy overview. For step-by-step instructions see the dedicated deployment pages: [Vercel](/docs/deployment/vercel.md), [Docker](/docs/deployment/docker.md), [Self-hosted](/docs/deployment/self-hosted.md), and [Environment configuration](/docs/deployment/environment.md).

### For founders

## Choosing a deployment target

### k3s cluster (production — recommended)

The primary production target is a Kubernetes cluster (`5.161.246.54`). All Ringdom empire clones run on this cluster.

- Full PostgreSQL with native WSS Tunnel (`TUNNEL_HUB_MODE=k8s-postgres`)
- All real-time features (messaging, collaboration, live notifications)
- WayForPay payment webhooks
- Cron jobs for username cleanup, refcodes minting

### Vercel (preview)

Vercel is suitable for preview deployments and rapid prototyping. Serverless route handlers run on Vercel Edge — the custom server (`server.ts`) is not used, so native WebSocket Tunnel is unavailable. Tunnel reverts to SSE + long-polling.

- Set `RING_DEPLOY_TARGET=vercel` and `NEXT_PUBLIC_RING_DEPLOY_TARGET=vercel`
- Use `DB_BACKEND_MODE=firebase-full` (Firestore) or `k8s-postgres-fcm` (requires a reachable PostgreSQL host)
- WayForPay webhooks require a public endpoint — Vercel functions handle this natively

### Docker (self-hosted)

Docker deployment is documented for community self-hosting (OSS repository). The `Dockerfile` builds a production image with the custom server (`server.ts`) and native WSS Tunnel support. PostgreSQL runs as a companion container or external service.

- Full feature parity with k3s deployment
- PostgreSQL is required (`DB_BACKEND_MODE=k8s-postgres-fcm`)
- See [Docker deployment](/docs/deployment/docker.md) and [Self-hosted deployment](/docs/deployment/self-hosted.md)

## Environment variable strategy

The single source of truth for all environment variables is `env.local.template` in the repository root. Key decisions:

| Variable | Purpose |
|----------|---------|
| `DB_BACKEND_MODE` | Database adapter: `k8s-postgres-fcm` (recommended), `firebase-full`, or `supabase-fcm` |
| `RING_DEPLOY_TARGET` | Tunnel and runtime mode: `k8s`, `vercel`, or `self-hosted` |
| `AUTH_SECRET` | Auth.js session encryption (generate with `openssl rand -base64 32`) |
| `AUTH_GOOGLE_ID` / `AUTH_GOOGLE_SECRET` | Google OAuth credentials |

See [Environment configuration](/docs/deployment/environment.md) for the complete variable reference.

> **Warning**
> Never commit real secrets to Git. Copy `env.local.template` to `.env.local` (gitignored) and fill in production values. On k3s, inject secrets via Kubernetes secrets; on Vercel, use the Vercel dashboard environment variables.

### For developers

## Prerequisites for all targets

- Node.js 20+ (22 recommended)
- npm or pnpm
- A PostgreSQL instance (for `k8s-postgres-fcm` and `supabase-fcm` modes)
- Firebase project (optional — only if FCM push notifications are needed)

## Deployment targets

### 1. k3s cluster (production)

The production k3s cluster uses Kubernetes manifests (not shipped in the public OSS repo). Deployment is managed via the Ring CLI and CI/CD pipeline.

Required environment variables for production:

```bash
# Database
DB_BACKEND_MODE=k8s-postgres-fcm
DB_HOST=postgres.ring-platform-org.svc.cluster.local
DB_PORT=5432
DB_NAME=ring_platform
DB_USER=ring_user
DB_PASSWORD=
DB_SSL=true
DB_POOL_SIZE=20
DB_TIMEOUT=30000

# Auth.js
AUTH_SECRET=
AUTH_GOOGLE_ID=
AUTH_GOOGLE_SECRET=

# Tunnel
RING_DEPLOY_TARGET=k8s
NEXT_PUBLIC_RING_DEPLOY_TARGET=k8s
TUNNEL_HUB_MODE=k8s-postgres

# Firebase Admin (FCM only — optional)
AUTH_FIREBASE_PROJECT_ID=
AUTH_FIREBASE_CLIENT_EMAIL=
AUTH_FIREBASE_PRIVATE_KEY=""

# Payments
WAYFORPAY_MERCHANT_ACCOUNT=
WAYFORPAY_SECRET_KEY=
```

In production, sensitive values are injected via Kubernetes secrets at runtime, not baked into the Docker image.

### 2. Vercel

Vercel deployment uses serverless route handlers. The custom server (`server.ts`) is not used — Tunnel operates in SSE + long-polling mode.

**vercel.json** (the platform ships with this exact file):

```json
{
  "crons": [
    {
      "path": "/api/cron/cleanup-usernames",
      "schedule": "*/5 * * * *"
    }
  ]
}
```

> **Tip**
> Set `RING_DEPLOY_TARGET=vercel` and `NEXT_PUBLIC_RING_DEPLOY_TARGET=vercel` in the Vercel dashboard. Without these, Tunnel may attempt native WebSocket initialization that is unavailable in serverless runtime.

Steps:

1. Push the repository to GitHub
2. Import the project in Vercel
3. Set environment variables in the Vercel dashboard (all `DB_*`, `AUTH_*`, `NEXT_PUBLIC_*`, `WAYFORPAY_*`, etc.)
4. Deploy the main branch

See [Vercel deployment](/docs/deployment/vercel.md) for details.

### 3. Docker (self-hosted)

The `Dockerfile` builds a multi-stage production image with the custom server and native WSS Tunnel. It uses `dumb-init` as the entrypoint and runs a health check on `/api/health`.

Build:

```bash
docker build \
  --platform linux/amd64 \
  --build-arg AUTH_SECRET="your-auth-secret" \
  --build-arg DB_BACKEND_MODE=k8s-postgres-fcm \
  --build-arg DB_HOST=postgres.example.com \
  --build-arg DB_PORT=5432 \
  --build-arg DB_NAME=ring_platform \
  --build-arg DB_USER=ring_user \
  --build-arg NEXT_PUBLIC_APP_URL=https://your-domain.com \
  --build-arg NEXT_PUBLIC_API_URL=https://your-domain.com \
  -t ring-platform .
```

> **Warning**
> Only build-time safe variables (`NEXT_PUBLIC_*`, `DB_BACKEND_MODE`) should be passed as build args. Sensitive secrets (`AUTH_GOOGLE_SECRET`, `DB_PASSWORD`, `WAYFORPAY_SECRET_KEY`) are injected at runtime via environment variables or secrets management.

Run:

```bash
docker run -d \
  -p 3000:3000 \
  -e AUTH_SECRET= \
  -e AUTH_GOOGLE_ID= \
  -e AUTH_GOOGLE_SECRET= \
  -e DB_BACKEND_MODE=k8s-postgres-fcm \
  -e DB_HOST=postgres.example.com \
  -e DB_PORT=5432 \
  -e DB_NAME=ring_platform \
  -e DB_USER=ring_user \
  -e DB_PASSWORD= \
  -e DB_SSL=true \
  -e RING_DEPLOY_TARGET=self-hosted \
  -e NEXT_PUBLIC_RING_DEPLOY_TARGET=self-hosted \
  ring-platform
```

See [Docker deployment](/docs/deployment/docker.md) for docker-compose configuration and PostGIS setup, and [Self-hosted deployment](/docs/deployment/self-hosted.md) for the community OSS setup with `install.sh`.

## Database setup across targets

The platform uses a single database abstraction layer via `getDatabaseService()` (see `lib/database/DatabaseService.ts`). The active adapter is selected by `DB_BACKEND_MODE`:

- **`k8s-postgres-fcm`** — PostgreSQL for all CRUD, Firebase Admin SDK for FCM push only. Recommended for production and local dev.
- **`firebase-full`** — Firestore (Firebase) for all CRUD. Fastest setup for prototyping and Vercel.
- **`supabase-fcm`** — Supabase PostgreSQL for database, Firebase Admin SDK for FCM push only.

See [Backend modes and databases](/docs/architecture/backend-modes-and-databases.md) for a deep comparison.

```bash
# Verify database connection (any mode)
# Uses getDatabaseService() — NOT a Firebase client SDK
# lib/firebase.ts does not exist in this codebase
```

> **Info**
> Firestore client SDK (`db.collection(...).doc(...).get()`) is not used in this platform. All database access goes through `getDatabaseService()` with methods `findById(collection, id)`, `create(collection, data)`, `update(collection, id, data)`, `delete(collection, id)`, and `query({ collection, filters?, orderBy?, pagination? })`.

## Troubleshooting

### Build fails with TypeScript errors

Clear caches and retry:

```bash
rm -rf .next node_modules
npm install
npm run type-check
npm run build
```

### Auth.js session not working

Verify `AUTH_SECRET` is set and consistent across deployments. Auth.js v5 uses `AUTH_SECRET` (not `NEXTAUTH_SECRET`). Google OAuth variables are `AUTH_GOOGLE_ID` and `AUTH_GOOGLE_SECRET` (not `GOOGLE_CLIENT_ID`).

### Database connection refused

Check that PostgreSQL is reachable from the deployment target:

```bash
# From k3s pod
kubectl exec deploy/ring-platform -- wget -qO- http://postgres:5432

# From Docker
docker exec ring-platform nc -zv postgres.example.com 5432
```

Validate that `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, and `DB_PASSWORD` match your PostgreSQL configuration.

### Tunnel / WebSocket not connecting

Verify `RING_DEPLOY_TARGET` and `NEXT_PUBLIC_RING_DEPLOY_TARGET` are set. On Vercel, native WebSocket is unavailable — Tunnel falls back to SSE + long-polling automatically. On k3s or Docker, ensure `TUNNEL_HUB_MODE=k8s-postgres` is set.

## Related documentation
