---
title: "Deploy Keyzori | (Just Updated) Self-Hosted Software License Key Server, Master Key Generated at Deploy"
description: "Keyzori license key server with REST API, Postgres, Redis and master key"
category: "Other"
url: https://railway.com/deploy/keyzori-or-just-updated-self-hosted-soft
---

# Deploy Keyzori | (Just Updated) Self-Hosted Software License Key Server, Master Key Generated at Deploy

Keyzori license key server with REST API, Postgres, Redis and master key

**[Deploy Keyzori | (Just Updated) Self-Hosted Software License Key Server, Master Key Generated at Deploy on Railway](https://railway.com/template/keyzori-or-just-updated-self-hosted-soft)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/keyzori-or-just-updated-self-hosted-soft/manifest.json

- **Creator:** SuperSlowSloth
- **Category:** Other

## Template content

### keyzori

- **Image:** ghcr.io/bon5co/keyzori-railway:2.0.3@sha256:2b26fa89a210febc14da9e5f0a42f3ed960ed280dc5169376d5c1c2a4768cd84
- **Health check:** /health
- **Public domain:** Yes

### redis

- **Image:** redis:8.2.1@sha256:5fa2edb1e408fa8235e6db8fab01d1afaaae96c9403ba67b70feceb8661e8621
- **Start command:** `/bin/sh -c 'exec docker-entrypoint.sh redis-server --requirepass "$REDIS_PASSWORD" --save "" --appendonly no'`

### postgres

- **Image:** ghcr.io/bon5co/keyzori-postgres-railway:18.0@sha256:5a498ee98bb837ae36c749005aa2ae896d021e754103e2d0a364cefa2c7c0ad2

## Documentation

# Deploy and Host Keyzori on Railway

**Built for an LLM agent to deploy and use.** The deploy form has no fields to fill: the root master key and both database passwords are generated at deploy time, PostgreSQL stores the licences on a volume, and every write answers `401` until the master key is sent. Keyzori is an open-source, self-hosted software licensing server: issue license keys with expiry dates, usage limits and device or IP caps, and check them from your app with one request.

## About Hosting Keyzori

This template runs three services: **keyzori** (the licensing API on one public domain), **postgres** (PostgreSQL 18 with a volume mounted at `/var/lib/postgresql`) and **redis** (Redis 8, password protected, no persistence, as upstream runs it). Licences, users, items and API keys live in PostgreSQL, so they survive redeploys of any service.

**Ready from boot.** Keyzori refuses to start without a root key of the exact shape `kz_key_` followed by 64 hexadecimal characters. This template generates it into the `KZ_MASTER_KEY` variable, and the database role the server uses is created on the first PostgreSQL boot, so a fresh deploy answers `/health` with PostgreSQL and Redis both up and needs no human step.

**Using it.** Create a licence with `POST /licenses`, enable it with `POST /licenses/enable` (new licences start disabled), then check it from your software with `POST /validate`. The create call returns the licence credential once; store it straight away. A rejected licence still answers HTTP 200, so read the `code` field (`VALID`, `LICENSE_DISABLED`, `LICENSE_INVALID`). Use the master key only to set up scoped API keys for everyday access.

### Agent contract

- Public URL: `https://RAILWAY_PUBLIC_DOMAIN` (the generated domain of the `keyzori` service)
- Private URL: `http://keyzori.railway.internal:8080`
- Credentials: the variable `KZ_MASTER_KEY` on the `keyzori` service (there is no username)
- Auth: send `Authorization: Bearer $KZ_MASTER_KEY` on every management call, with a unique `Idempotency-Key` header on writes; a write without the key or with a wrong key answers `401`. `/validate` needs no key, it takes the licence credential in the body.
- Health: `GET /health` returns 200 with `{"healthy":true,"postgresql":true,"redis":true}`

```bash
#!/bin/sh
# needs: SITE_URL (public https URL of the keyzori service) and KZ_MASTER_KEY (its variable); also curl, grep, cut
H="Content-Type: application/json"
A="Authorization: Bearer $KZ_MASTER_KEY"
K="kz-check-$$-$(date +%s)"
curl -s -w "\nhealth: %{http_code}\n" "$SITE_URL/health"
curl -s -o /dev/null -w "write without key: %{http_code}\n" -X POST -H "$H" -H "Idempotency-Key: $K-a" -d '{}' "$SITE_URL/licenses"
curl -s -o /dev/null -w "write with wrong key: %{http_code}\n" -X POST -H "$H" -H "Idempotency-Key: $K-b" -H "Authorization: Bearer kz_key_0000000000000000000000000000000000000000000000000000000000000000" -d '{}' "$SITE_URL/licenses"
R=$(curl -s -X POST -H "$H" -H "$A" -H "Idempotency-Key: $K-c" -d '{}' "$SITE_URL/licenses")
ID=$(echo "$R" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
CRED=$(echo "$R" | grep -o '"credential":"[^"]*"' | cut -d'"' -f4)
echo "license: ${ID:+created}"
curl -s -o /dev/null -w "enable: %{http_code}\n" -X POST -H "$H" -H "$A" -H "Idempotency-Key: $K-d" -d "{\"ids\":[\"$ID\"]}" "$SITE_URL/licenses/enable"
echo "validate: $(curl -s -X POST -H "$H" -d "{\"license\":\"$CRED\"}" "$SITE_URL/validate" | grep -o '"code":"[A-Z_]*"')"
curl -s -o /dev/null -w "disable: %{http_code}\n" -X POST -H "$H" -H "$A" -H "Idempotency-Key: $K-e" -d "{\"ids\":[\"$ID\"]}" "$SITE_URL/licenses/disable"
echo "validate after disable: $(curl -s -X POST -H "$H" -d "{\"license\":\"$CRED\"}" "$SITE_URL/validate" | grep -o '"code":"[A-Z_]*"')"
```

A fresh deploy prints the health JSON, `health: 200`, `write without key: 401`, `write with wrong key: 401`, `license: created`, `enable: 200`, `validate: "code":"VALID"`, `disable: 200` and `validate after disable: "code":"LICENSE_DISABLED"`.

## Why Deploy Keyzori on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying Keyzori on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.

- **Agent-ready** - no deployer fields, a contract block and one script that proves the refusal without a key, a create, an enable, a validate and a disable.
- **Locked by default** - the API answers 401 to every write until the generated master key is sent.
- **Data survives redeploys** - licences sit in PostgreSQL on a volume, and the server connects as a restricted database role rather than the administrator.
- **Pinned** - the upstream image and both database images are pinned by digest.
- **No per-licence pricing** - unlimited licences on your own domain.

## Common Use Cases

- **License keys for a desktop or CLI app** - issue a key per customer and validate it at startup.
- **Trials and expiring licences** - set an expiry date and let validation refuse the key afterwards.
- **Seat and device limits** - cap devices or IP addresses per licence.
- **Usage-metered features** - count requests against a licence meter from your backend.

## Dependencies for Keyzori Hosting

- The keyzori service, a PostgreSQL service with a volume and a Redis service, all created by the template. A custom domain is optional.

### Deployment Dependencies

- [Keyzori repository](https://github.com/keyzori/Keyzori)
- [Official Docker image](https://github.com/keyzori/Keyzori/pkgs/container/server)
- [Wrapper image source](https://github.com/bon5co/keyzori-railway)


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/keyzori-or-just-updated-self-hosted-soft
