Skip to content
Skip to content
Get started
/Developers

Developers

Self-hosting

zcrypt is open source. Run your own backend with Docker for full control over the database, master key, and storage. The frontend is a standard Next.js app you can deploy anywhere.

Overview

A zcrypt deployment is two pieces: a stateless Go backend (the API, built into a tiny distroless container image) and a Next.js frontend. The backend talks to a PostgreSQL database and is configured entirely through environment variables. It holds no plaintext and no encryption keys (encryption and decryption happen in the browser) so the only long-lived secret you must protect is the MASTER_KEY, which wraps your connected storage tokens at rest.

A documented template with placeholder values lives at app/backend/.env.example. Copy it to app/backend/.env and fill in your own values: the .env file is gitignored, so never commit real secrets.

Prerequisites

  • •PostgreSQL: a local instance or a managed provider such as Neon. The schema is applied automatically on first boot (or run the migrate subcommand).
  • •Docker: the simplest way to run the backend. The repository ships a multi-stage Dockerfile at its root.
  • •Go 1.26+: only needed if you build the backend from source instead of using Docker.
  • •Node.js 20+ and Bun: for building or running the frontend.

Run with Docker

Build the image from the repository root, then run it with your configuration passed as environment variables. At minimum you need a database connection, a master key, a JWT secret, and your frontend origin.

shell
# Build from the repo root (the Dockerfile lives there)
docker build -t zcrypt .

# Run it
docker run -p 8080:8080 \
  -e DATABASE_URL="postgresql://user:pass@host/db?sslmode=require" \
  -e MASTER_KEY="$(openssl rand -hex 32)" \
  -e ZCRYPT_JWT_SECRET="$(openssl rand -hex 32)" \
  -e FRONTEND_URL="https://your-frontend.example.com" \
  -e ALLOWED_ORIGINS="https://your-frontend.example.com" \
  zcrypt

Prefer an env file? Pass --env-file app/backend/.env instead of repeating -e flags. The container listens on 8080 by default; hosts that inject a PORT variable are honoured too. To run it on a Linux server behind a Cloudflare Tunnel (no open ports, no certificates to manage), follow deploy/server/README.md in the repository: it ships a Docker Compose file and a setup script.

Guard the MASTER_KEY

The MASTER_KEY is a 32-byte hex value that encrypts every stored platform token at rest. Generate a fresh one with openssl rand -hex 32 and keep it safe. If you lose it, connected storage tokens can no longer be decrypted; if it leaks, those tokens are exposed. It never encrypts your files. Those are sealed in the browser under your passphrase.

Required environment variables

These must be set for a production deployment. The server refuses to start without DATABASE_URL and MASTER_KEY.

VariableDescription
DATABASE_URLPostgreSQL connection string (e.g. a Neon serverless database). The schema migrates automatically on startup.
MASTER_KEY32-byte hex key (64 hex chars) for envelope encryption of platform tokens at rest. Generate with openssl rand -hex 32.
ZCRYPT_JWT_SECRETSecret used to sign access and refresh tokens (HS256). Must be at least 32 characters. If left empty the server auto-generates an ephemeral one, so set a stable value in production or tokens stop validating after a restart.
FRONTEND_URLPublic URL of your frontend. Used for email links and the post-OAuth redirect, and added to the CORS allow-list automatically. No trailing slash.
ALLOWED_ORIGINSComma-separated list of origins allowed by CORS. Defaults to localhost if unset; FRONTEND_URL is always added on top.

Optional environment variables

Everything below is optional. OAuth and email each switch on only when their full set of values is present.

VariableDescription
BACKEND_URLPublic backend URL (e.g. https://api.example.com), no trailing slash. Required for OAuth: it builds the redirect_uri and must exactly match what is registered with Google/GitHub. If unset it is derived per-request, which usually breaks OAuth.
ZCRYPT_PORTPort to listen on (default 8080). PORT is also honoured for cloud hosts that inject it.
ZCRYPT_TRUSTED_PROXY_COUNTNumber of trusted reverse-proxy hops in front of the app. The real client IP is read that many hops in from the right of the X-Forwarded-For chain. 0 (default) ignores forwarding headers so clients cannot spoof their IP to bypass rate limits; set 1 behind a single proxy such as a Cloudflare Tunnel or a load balancer (the bundled deploy/server setup uses 1).
RESEND_API_KEYResend API key. Enables transactional email (verification, password reset, magic links). When set, RESEND_FROM is also required.
RESEND_FROMFrom address for emails, e.g. zcrypt <noreply@example.com>.
DEV_MODESet to true only for local load testing, it disables all rate limiting. Never enable it in production.

Email is optional, but recommended

Without RESEND_API_KEY the server runs fine, but it cannot send verification or password-reset emails. If you configure email, both the API key and the from-address are validated at startup.

OAuth (Google / GitHub)

Sign-in with Google or GitHub is optional. A provider is enabled only when both its client ID and secret are present.

VariableDescription
GOOGLE_CLIENT_IDGoogle OAuth client ID.
GOOGLE_CLIENT_SECRETGoogle OAuth client secret.
GITHUB_CLIENT_IDGitHub OAuth app client ID.
GITHUB_CLIENT_SECRETGitHub OAuth app client secret.

Register these exact redirect URIs with each provider, substituting your real BACKEND_URL:

redirect URIs
# Google -> Authorized redirect URIs
https://<BACKEND_URL>/api/auth/oauth/google/callback

# GitHub -> Authorization callback URL
https://<BACKEND_URL>/api/auth/oauth/github/callback

The backend logs the exact URIs to register at startup, and serves them (with no secrets) at GET /api/auth/oauth/config so you can verify the live configuration.

Frontend

The frontend only needs to know where the backend lives. Point it at your running backend, then run the dev server or produce a production build.

shell
cd app/frontend

# Install dependencies
bun install

# Point the frontend at your backend
echo "NEXT_PUBLIC_API_URL=http://localhost:8080" > .env.local

# Dev server
bun run dev

# Production build
bun run build
VariableDescription
NEXT_PUBLIC_API_URLBase URL of your zcrypt backend API. Baked in at build time, so rebuild after changing it.

Where to go next