JellyboxJellybox/Docs
← Self-hosting guide

Deploy the server

The Jellybox server is a Next.js application. This guide deploys it to Vercel with a Neon PostgreSQL database — both have generous free tiers that are more than enough for a home setup. Neon is just one option; any PostgreSQL database (self-hosted, Supabase, Railway, RDS, etc.) will work — anywhere you can point DATABASE_URL at a Postgres instance.

Before you start

  • A GitHub account
  • A Vercel account (free)
  • A Neon account (free) — neon.tech
  • A Resend account (free) — resend.com — for email verification
  • Optionally: a Google Cloud project for Google sign-in
1

Fork the repository

Go to github.com/Nikorag/Jellybox-Server and click Fork. You need your own fork so Vercel can deploy from it and so you can push any local customisations.

2

Create a Neon database

Sign in at neon.techand create a new project. Choose a region close to where you'll deploy on Vercel (e.g. EU West or US East).

From the project dashboard, copy the Connection string. It looks like:

postgresql://username:password@ep-xxx.region.aws.neon.tech/neondb?sslmode=require

Save this — it becomes your DATABASE_URL.

3

Generate your secrets

You need two secrets. Run these commands locally (requires openssl, which is pre-installed on macOS and most Linux systems):

AUTH_SECRET — for signing session tokens:

openssl rand -base64 32

JELLYFIN_ENCRYPTION_KEY — for encrypting your Jellyfin credentials:

openssl rand -hex 32

Copy both outputs and keep them somewhere safe.

4

Get a Resend API key

Jellybox sends a verification email when users create an account. Sign in at resend.com, go to API Keys, and create a key with Sending access. Copy the key — it starts with re_.

On Resend's free plan you can send from onboarding@resend.devwithout verifying a domain. That's enough to get started.
5

Set up Google OAuth (optional)

This enables “Continue with Google” on the sign-in and sign-up pages. Skip this step if you only want email/password accounts.

  1. Go to console.cloud.google.com and create a project.
  2. Navigate to APIs & Services → Credentials.
  3. Click Create Credentials → OAuth client ID.
  4. Choose Web application.
  5. Add your Vercel domain to Authorised JavaScript origins:
    https://your-app.vercel.app
  6. Add this to Authorised redirect URIs:
    https://your-app.vercel.app/api/auth/callback/google
  7. Copy the Client ID and Client Secret.
You'll need to come back and add the final Vercel URL after deploying in step 6.
6

Deploy to Vercel

Go to vercel.com/new and import your forked repository. Vercel will detect it as a Next.js project automatically.

Before clicking Deploy, add all your environment variables (see step 7). Vercel will inject them at build time.

7

Add environment variables

In Vercel's project settings under Environment Variables, add:

DATABASE_URL

Your Neon connection string.

postgresql://user:pass@ep-xxx.region.aws.neon.tech/neondb?sslmode=require

AUTH_SECRET

32-byte base64 string for signing session JWTs. Generated in step 3.

JELLYFIN_ENCRYPTION_KEY

64-character hex key for AES-256-GCM encryption of Jellyfin credentials. Generated in step 3.

RESEND_API_KEY

Resend API key for sending verification and reset emails.

re_xxxxxxxxxxxxxxxxxx

NEXTAUTH_URLoptional

Your full public URL. Vercel sets this automatically — you usually don't need to add it manually.

https://your-app.vercel.app

AUTH_GOOGLE_IDoptional

Google OAuth client ID. Only required if you set up Google sign-in in step 5.

AUTH_GOOGLE_SECREToptional

Google OAuth client secret. Only required if you set up Google sign-in in step 5.

FIRMWARE_REPOoptional

GitHub owner/name of the firmware repo your install polls for OTA. Defaults to Nikorag/Jellybox-Firmware. Override if you maintain your own firmware fork.

Nikorag/Jellybox-Firmware

FIRMWARE_VERSIONoptional

Pin every paired device to a specific firmware tag instead of the latest GitHub release. Leave unset (or `latest`) to always serve the newest release.

v0.0.2

Background: paired devices poll /api/device/me every 30s and report their running firmware version. Updates are triggered by hand from the device page in the dashboard; the server fetches the GitHub release manifest every 5 minutes so it knows what the latest version is. See the firmware OTA section for the end-to-end flow.
8

Run the database migration

After the first deploy, the database tables need to be created. Clone your fork locally and run:

cd apps/server
cp .env.example .env.local
# Fill in DATABASE_URL in .env.local, then:
npm install
npm run db:migrate

This creates all the necessary tables (users, devices, tags, etc.) in your Neon database. You only need to do this once; future schema changes will apply automatically on deploy via the migration files in prisma/migrations/.

You can also run npm run db:migrate from a Vercel build hook or a GitHub Action if you want fully automated deployments.
9

Done — create your account

Visit your Vercel URL, click Create account, and sign up. Once you're in, go to the Jellyfin section and connect your server.

Next: build your first device →

Device log bridge

Jellybox firmware broadcasts its log output over the LAN as UDP packets — see Device log streaming on the firmware page for the wire format. UDP broadcasts don't leave the local network segment, so a Vercel-hosted (or otherwise remote) server can't see them directly. The log bridgeis a small Node process that listens for those broadcasts and forwards each line over HTTPS to the server's ingest endpoint, where any admin watching /dashboard/device-logs sees them appear live over SSE. Nothing is persisted — the server just fans lines out to connected viewers.

Source and Dockerfile live in apps/server/utils/log-bridge/.

1. Turn the feature on

Add these to your server environment:

DEVICE_LOGS_ENABLED

Exposes the ingest endpoint and the admin-only Device logs page.

true

DEVICE_LOGS_INGEST_TOKEN

Long random bearer token. The bridge must send this on every POST.

ADMINS

Comma-separated emails allowed to view device logs. The nav entry is hidden for everyone else.

you@example.com

2. Run the bridge on your LAN

Pick whichever option matches your setup. The bridge must be on the same broadcast domain as the devices — running it on Vercel won't work, since Vercel functions can't receive UDP from your home network.

Sidecar (self-hosted server)— if you're running the Jellybox server on a box you own (NAS, home server, VPS with a VPN back to your LAN), run the bridge as another container alongside it. An already-wired service block lives commented-out in the repo's docker-compose.yml; uncomment it, set the two env vars, and docker compose up -d.

Standalone container (Vercel deploy) — when the server lives on Vercel, run the bridge by itself on any always-on machine on the device LAN: a Raspberry Pi, NAS, mini-PC, or a spare VM. Build and run with host networking so it can actually see broadcast traffic — Docker bridge networking won't receive the packets:

cd apps/server/utils/log-bridge
docker build -t jellybox-log-bridge .

docker run -d --restart unless-stopped \
  --network host \
  --name jellybox-log-bridge \
  -e JELLYBOX_URL=https://your-app.vercel.app \
  -e INGEST_TOKEN=<same value as DEVICE_LOGS_INGEST_TOKEN> \
  jellybox-log-bridge

Or without Docker, straight from a checkout:

cd apps/server/utils/log-bridge
JELLYBOX_URL=https://your-app.vercel.app \
INGEST_TOKEN=<token> \
node index.js
Host networking is only available on Linux Docker hosts. On Docker Desktop for macOS or Windows, run the bridge directly with node instead — bridge networking on those platforms silently drops broadcast packets.

Once the bridge is running, open /dashboard/device-logs while signed in as one of the ADMINS emails. Trigger any action on a device (a scan, a reboot) and the lines should arrive within a second or two. For a smoke test without a real device:

echo "12345 hello from a fake device" | nc -u -w1 -b 255.255.255.255 5514