Self-hosting
What the self-hosted edition includes, what it needs to run, and its support status.
Voidhash comes in two editions: Voidhash Cloud, which we run for you, and the self-hosted Community edition, which you run from the public source on your own infrastructure. Both use the same SDKs, CLI and API.
Self-hosting is in alpha and not yet supported for production use. Only the latest main branch
receives security fixes, and there is no official container image or deployment template yet. A
production support matrix will be published before the first public release.
What's included
The Community edition runs Studio, the API, the SDK endpoints, store and Stripe processing, payment sources, webhooks, perks and access checks, people, the built-in analytics reports, VoidQL through the API and the MCP tools, the CLI and the MCP server.
It doesn't include:
- Billing. There are no plans, usage meters or spend caps.
- Insights, Dashboards and Studio's Query page. Only the built-in analytics pages are available. VoidQL queries still run through the API and the MCP tools.
- Hosted tracking domains. Point the web SDK at your own API domain instead; see Tracking domain.
- Paywall location statistics and A/B test summary metrics.
- Early-access features. Features that are in early access on Cloud, such as paywalls, feature flags, A/B tests, automations and push notifications, stay off: a self-hosted server has no supported way to turn them on.
Several built-in events are off by default on a self-hosted server, including $screen,
$pageview, $app_opened and the paywall events. Turn on the ones you need in
Settings → Events. See Events.
Run it locally
An evaluation install runs everything on your machine from a checkout of the public source, with PostgreSQL, ClickHouse, Redis and S3-compatible storage in Docker. It signs you in with a local root account, so it serves only loopback addresses.
Install the tools
You need Node.js 24 or later, Bun, git and a running Docker. pnpm 11.21.0 comes
through Corepack. Rust with the wasm32-unknown-unknown target is optional:
without it, custom component builds and the paywall designer canvas don't work.
Clone and install
git clone https://github.com/voidhashcom/voidhash.git
cd voidhash
corepack enable
corepack prepare pnpm@11.21.0 --activate
pnpm install --frozen-lockfileConfigure
cp .env.example .env.local
openssl rand -base64 32Paste the generated value into .env.local as ENCRYPTION_KEY. It must be 32 bytes, base64
encoded; without it, Voidhash stores store and payment credentials as plain text. The file's other
defaults work as they are, including the root account root / voidhash. Change
VOIDHASH_ROOT_PASSWORD if you like.
Start the stack
pnpm vh doctor
pnpm devpnpm vh doctor checks the tools, Docker and your .env.local, and prints a fix for each
problem. pnpm dev starts PostgreSQL 16, ClickHouse 25.8, Redis 7.4 and RustFS (S3-compatible
storage) in Docker, creates the database and bucket, applies the database migrations, and starts
the API, the worker and Studio. In a terminal it shows each service and its log; q or
Ctrl-C stops the services and keeps your data.
| Service | Address | Started with |
|---|---|---|
| Studio | http://127.0.0.1:3000 | node scripts/dev.mjs in apps/www |
| API | http://127.0.0.1:8788 | bun src/main.ts in apps/backend |
| Worker | No public address | bun src/worker.ts in apps/backend |
| Migrations | — | pnpm db:migrate, on every start |
If another program holds these ports, every port moves up by 100. pnpm vh status prints the
addresses in use.
Sign in
Open Studio at http://127.0.0.1:3000 and sign in with Username root and Password
voidhash, or the values you set. Create a team and a project, then copy the publishable keys from
Settings → API keys. There is no waitlist.
Point an SDK at it
Set the SDK's base URL to the API address, http://127.0.0.1:8788; see
Point SDKs at your server. An iOS simulator reaches it at that
address. An Android emulator reaches it at http://10.0.2.2:8788, and Android blocks plain HTTP
unless a debug network security config allows cleartext traffic to that host. A physical device
can't reach a loopback address.
To upgrade, run git pull and pnpm install --frozen-lockfile, then pnpm dev, which applies new
migrations. pnpm vh down stops the stack from another terminal; pnpm vh down --wipe also
deletes its data. To upgrade other installs, back them up or rotate their secrets, see
Operate a self-hosted server.
Sign-in
The AUTH_PROVIDER setting chooses how people sign in to Studio.
| Value | What you get |
|---|---|
local | A single root account from VOIDHASH_ROOT_USERNAME and VOIDHASH_ROOT_PASSWORD. No sign-up and no other team members. Works only on a loopback address (127.0.0.1, localhost, ::1) and never with NODE_ENV=production. |
workos | Email and password sign-up, GitHub sign-in, team members and invitations, and SSO and directory sync. Needs your own WorkOS account (WORKOS_CLIENT_ID, WORKOS_API_KEY). |
Any deployment that others reach over a network therefore needs workos.
Requirements
| Component | Requirement |
|---|---|
| API and worker | Two Bun processes built from the source; Node.js 24 or later and pnpm to build. |
| Studio | A web server process from the same source. |
| PostgreSQL | Development and tests use PostgreSQL 16. |
| ClickHouse | 25.8 or later, reached over HTTP, with at least 2 GB of memory. Every API and worker process needs it. |
| Redis | Reached through BACKEND_REDIS_URL. |
| S3-compatible storage | One bucket for uploaded files and paywall assets. |
Configuration
Configuration comes from environment variables. The repository's .env.example shows a working
local setup, and .env.schema lists every supported setting. The main ones:
| Variable | Purpose |
|---|---|
BACKEND_PUBLIC_URL | The public origin of your API, which SDKs and webhooks use. |
BACKEND_DASHBOARD_URL | The public origin of Studio. |
BACKEND_DATABASE_* | PostgreSQL host, port, database name, user and password. |
BACKEND_REDIS_URL | Redis connection URL. |
BACKEND_S3_* | Bucket, region, endpoint and credentials of your object storage. |
ANALYTICS_CLICKHOUSE_URL, ANALYTICS_CLICKHOUSE_* | ClickHouse URL, database, user and password. |
ANALYTICS_CLICKHOUSE_READER_*, ANALYTICS_CLICKHOUSE_AGENT_* | Optional read-only users for reports and VoidQL. See Analytics users. |
AUTH_PROVIDER, VOIDHASH_AUTH_SECRET | Sign-in provider and the session secret both processes share. |
ENCRYPTION_KEY | Encrypts stored store, payment and push credentials. Keep it safe; you can't read those credentials without it. |
EXCHANGE_RATE_API_KEY | Converts revenue in other currencies. |
The API, the worker and Studio must use the same settings.
Point SDKs at your server
The SDKs and libraries default to Voidhash Cloud. Set each one's base URL to your
BACKEND_PUBLIC_URL. The client SDKs send events to the base URL too. The server libraries send
events to their own default, https://ingest.voidhash.com, whatever the base URL, so set their
ingest URL to the same origin:
import Foundation
import Voidhash
var options = VoidhashOptions()
options.baseUrl = URL(string: "https://api.example.com")!
Voidhash.configure(publishableKey: "vh_pk_...", options: options)import com.voidhash.sdk.Voidhash
import com.voidhash.sdk.VoidhashOptions
Voidhash.configure(
context = this,
publishableKey = "vh_pk_...",
options = VoidhashOptions(baseUrl = "https://api.example.com"),
)import { createVoidhashClient } from "@voidhash/react-native";
export const voidhash = createVoidhashClient("vh_pk_...", {
baseUrl: "https://api.example.com",
});import { createVoidhashClient } from "@voidhash/web";
export const voidhash = createVoidhashClient({
publishableKey: "vh_pk_...",
baseUrl: "https://api.example.com",
});import { createVoidhashSdk } from "@voidhash/node";
export const voidhash = createVoidhashSdk({
secretKey: process.env.VOIDHASH_SECRET_KEY!,
baseUrl: "https://api.example.com",
ingestUrl: "https://api.example.com",
});use Voidhash\VoidhashClient;
$voidhash = VoidhashClient::create(getenv('VOIDHASH_SECRET_KEY'), [
'baseUrl' => 'https://api.example.com',
'ingestUrl' => 'https://api.example.com',
]);client, err := voidhash.New(
os.Getenv("VOIDHASH_SECRET_KEY"),
voidhash.WithBaseURL("https://api.example.com"),
voidhash.WithIngestURL("https://api.example.com"),
)let voidhash = VoidhashClient::builder()
.secret_key(std::env::var("VOIDHASH_SECRET_KEY")?)
.base_url("https://api.example.com")
.ingest_url("https://api.example.com")
.build()?;For the CLI, set VOIDHASH_API_URL and sign in again. For the MCP server, use
https://api.example.com/api/mcp.
Licensing
| Code | License |
|---|---|
| Client SDKs, server libraries and the CLI | MIT |
| The API, worker and Studio | AGPL-3.0-only |
Apps that embed a Voidhash SDK aren't affected by the AGPL. You may modify and self-host the AGPL code, including commercially. If you run a modified version as a network service, the AGPL requires you to offer its source to its users. The license files in the repository are authoritative; this summary isn't legal advice.
Next steps
Something wrong or missing on this page? Tell us by email or on Discord.