Skip to content

Production Deployment

Jinbocho is deployed as a self-hosted stack on your own server. Documented below: the fastest way to get a working stack, followed by the fully manual, step-by-step version for when you want to control every piece yourself.

All deployment tooling (compose files, env templates, scripts) lives in jinbocho-install-community-v1.

Self-hosted deploy

Quick start

One command turns a fresh Debian/Ubuntu server into a fully running Jinbocho stack: one unified Postgres database, the three backend services, the frontend, and Caddy as a reverse proxy with automatic HTTPS.

git clone https://github.com/jinbocho/jinbocho-install-community-v1.git
cd jinbocho-install-community-v1

sudo ./scripts/setup-vps-community.sh \
  --domain library.example.com \
  --email you@example.com \
  --google-books-key AIza...

Requirements:

  • A fresh Debian/Ubuntu server with a public IP, run as root (or via sudo).
  • --domain must already resolve to the server's IP — Caddy requests a Let's Encrypt certificate for it on first start. Omit --domain/--email entirely to serve over plain HTTP on the bare IP instead (no TLS).
  • Any option not passed as a flag is asked interactively. Add --non-interactive to skip every prompt and rely only on flags/defaults — useful for unattended installs (e.g. cloud-init).

Within a couple of minutes the script prints the frontend URL, the API gateway URL, and where secrets were written. Open the frontend and register the first family — it becomes the admin account.

Re-running is safe

The script is idempotent: existing secrets, envs/*.env files, and the generated Caddyfile are kept as-is on a second run, unless you delete them first.

What the script does

scripts/setup-vps-community.sh runs docker/docker-compose.all.yml end-to-end:

  1. Installs Docker (via get.docker.com) if it isn't already present.
  2. Optionally configures ufw to open 22/80/443 (--enable-firewall).
  3. Clones (or updates) jinbocho-fe next to the infrastructure repo.
  4. Generates POSTGRES_PASSWORD, JWT_SECRET_KEY, and INTERNAL_SERVICE_TOKEN, and writes every envs/*.env file from the *.example templates.
  5. Writes a Caddyfile that reverse-proxies /api/* to the gateway and everything else to the frontend, with automatic TLS if --domain was set.
  6. Pulls the backend images from GHCR, builds the frontend image from source, and brings the whole stack up with docker compose ... up -d.
  7. Polls /health through the gateway and reports whether the stack came up cleanly.

All script options

Flag Value Default Description
--domain <fqdn> — (uses server IP, HTTP only) Public domain already pointed at this server. Enables automatic HTTPS.
--email <email> Let's Encrypt contact email. Required if --domain is set.
--google-books-key <key> Google Books API key, used for catalog ISBN lookups. Can be added later by editing envs/catalog-service.env.
--smtp-user <gmail address> Gmail address used to send invite/reset-password emails. SMTP host/port are set automatically. Leave unset to fall back to logging the link instead of sending it.
--smtp-password <app password> Gmail App Password for --smtp-user — not your normal account password.
--email-from <email> value of --smtp-user From-address shown on outgoing emails.
--netdata-enabled true|false asked interactively Enable the local Netdata dashboard (host + per-container metrics, :19999, localhost-only). No account or secrets needed. Optional, off by default.
--frontend-base-url <url> derived from --domain/server IP Public frontend URL baked into email links.
--fe-repo <git url> jinbocho/jinbocho-fe Frontend repository to clone.
--fe-branch <branch> main Frontend branch to clone.
--version <tag> latest Image tag to pull for the backend services.
--enable-firewall flag off Configure and enable ufw, opening 22/80/443.
--skip-docker-install flag off Don't attempt to install Docker (use if it's already provisioned by other tooling).
--non-interactive flag off Never prompt; use only the flags/defaults given.
-h, --help flag Print the full flag list and exit.

Verifying the deployment

./scripts/validate-api.sh

This registers a test family and exercises the main endpoints through the gateway (http://localhost:8000, or your domain).

Useful day-2 commands (the script prints the exact invocation for your setup, including --profile observability if Netdata is enabled):

docker compose -f docker/docker-compose.all.yml --env-file .env logs -f   # tail logs
docker compose -f docker/docker-compose.all.yml --env-file .env ps        # service status
docker compose -f docker/docker-compose.all.yml --env-file .env down      # stop (volumes/data kept)

Manual install (no installer script)

If you'd rather not run the automated script — for example to review every file before it's written, or because you already manage TLS with your own reverse proxy — install piece by piece with docker-compose.community.yml instead:

git clone https://github.com/jinbocho/jinbocho-install-community-v1.git
cd jinbocho-install-community-v1

cp .env.example .env
cp envs/auth-service.env.example envs/auth-service.env
cp envs/catalog-service.env.example envs/catalog-service.env
cp envs/api-gateway.env.example envs/api-gateway.env

# edit the files above — see the field tables below

docker compose -f docker/docker-compose.community.yml up -d

No TLS or reverse proxy in this mode

Unlike the one-shot script, docker-compose.community.yml does not run Caddy. The gateway is exposed directly on port 8000. Put your own reverse proxy (Caddy, nginx, Traefik, a load balancer) in front of it if you need HTTPS or a domain name.

.env (repo root, read by Docker Compose itself):

Variable Default Required Description
POSTGRES_PASSWORD change_me_local_dev Yes Password for the local Postgres containers. Change it before exposing anything publicly.
JINBOCHO_VERSION latest No Image tag to pull for the backend services.

envs/auth-service.env:

Variable Required Description
DEBUG No Set false in production; true also enables SQL logging.
DATABASE_URL Yes postgresql+asyncpg://auth_role:<POSTGRES_PASSWORD>@postgres:5432/jinbocho — the postgres compose service, database jinbocho, schema auth. Password matches POSTGRES_PASSWORD from the root .env.
JWT_SECRET_KEY Yes Must be identical across auth-service, catalog-service, and api-gateway. Generate with openssl rand -hex 32.
INTERNAL_SERVICE_TOKEN Yes Must match catalog-service's value — authenticates catalog→auth calls (loan reminder emails). Generate with openssl rand -hex 32.
FRONTEND_BASE_URL No Used to build links in invite/reset-password emails.
SMTP_USER / SMTP_PASSWORD No Leave empty to print invite/reset links to logs instead of emailing them.

envs/catalog-service.env:

Variable Required Description
DATABASE_URL Yes postgresql+asyncpg://catalog_role:<POSTGRES_PASSWORD>@postgres:5432/jinbocho — schema catalog.
JWT_SECRET_KEY Yes Identical to auth-service's value.
INTERNAL_SERVICE_TOKEN Yes Identical to auth-service's value.
GOOGLE_BOOKS_API_KEY Recommended Free key at console.cloud.google.com. Without it the shared quota (1000 req/day) is exhausted quickly.

envs/api-gateway.env:

Variable Required Description
JWT_SECRET_KEY Yes Identical to auth-service's value.
CORS_ORIGINS No ["*"] by default — set to your frontend's real URL in production.

Open http://<server-ip>:8000/docs to confirm the gateway is up, then proceed as in Verifying the deployment above.

Optional: metrics (Netdata)

Off by default — skip this if you don't need it, the stack works exactly as described above without it. When enabled (--netdata-enabled true, or by hand later with --profile observability), a single Netdata container monitors host + per-container CPU/RAM/disk/network with a built-in dashboard. No external account, no secrets to configure. The dashboard binds to 127.0.0.1:19999 only — reach it via ssh -L 19999:localhost:19999 <host>, then http://localhost:19999. See README.md (section 6) in jinbocho-install-community-v1 for the full setup.

Next Steps