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
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.
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.
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.
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_.
onboarding@resend.devwithout verifying a domain. That's enough to get started.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.
- Go to console.cloud.google.com and create a project.
- Navigate to APIs & Services → Credentials.
- Click Create Credentials → OAuth client ID.
- Choose Web application.
- Add your Vercel domain to Authorised JavaScript origins:
https://your-app.vercel.app - Add this to Authorised redirect URIs:
https://your-app.vercel.app/api/auth/callback/google - Copy the Client ID and Client Secret.
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.
Add environment variables
In Vercel's project settings under Environment Variables, add:
DATABASE_URLYour Neon connection string.
postgresql://user:pass@ep-xxx.region.aws.neon.tech/neondb?sslmode=require
AUTH_SECRET32-byte base64 string for signing session JWTs. Generated in step 3.
JELLYFIN_ENCRYPTION_KEY64-character hex key for AES-256-GCM encryption of Jellyfin credentials. Generated in step 3.
RESEND_API_KEYResend API key for sending verification and reset emails.
re_xxxxxxxxxxxxxxxxxx
NEXTAUTH_URLoptionalYour full public URL. Vercel sets this automatically — you usually don't need to add it manually.
https://your-app.vercel.app
AUTH_GOOGLE_IDoptionalGoogle OAuth client ID. Only required if you set up Google sign-in in step 5.
AUTH_GOOGLE_SECREToptionalGoogle OAuth client secret. Only required if you set up Google sign-in in step 5.
FIRMWARE_REPOoptionalGitHub 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_VERSIONoptionalPin 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
/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.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/.
npm run db:migrate from a Vercel build hook or a GitHub Action if you want fully automated deployments.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.
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_ENABLEDExposes the ingest endpoint and the admin-only Device logs page.
true
DEVICE_LOGS_INGEST_TOKENLong random bearer token. The bridge must send this on every POST.
ADMINSComma-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
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