This guide takes you from clone to a fully running local stack.
Prerequisites
- Bun
>= 1.3.5 - Rust — latest stable
- PostgreSQL 15+ (local or remote)
- Docker & Docker Compose (optional, for one-command startup)
1. Clone and install dependencies
git clone https://github.com/xuerzong/openproxy.git
cd openproxy
bun install
2. Generate an RSA key pair
Provider API keys are stored RSA-encrypted in the database. Generate a key pair with:
bun scripts/generateRSAKey.ts
Keep the output — you'll need both values in the next step.
3. Configure environment variables
apps/server
cp apps/server/.env.example apps/server/.env
If you are following the Docker deployment flow, prefer cd docker && ./prepare.sh to generate docker/.env instead of filling Docker environment variables by hand.
Edit apps/server/.env with at minimum:
DATABASE_URL=postgres://user:password@localhost:5432/openproxy
BETTER_AUTH_SECRET=<random 32-byte base64 string>
BETTER_AUTH_URL=http://localhost:5173/api
# APP_DOMAIN is the apex domain (e.g. example.com; defaults to aiproxy.shop).
# It controls:
# - trusted origins generated for *.APP_DOMAIN
# - better-auth session cookie Domain (.APP_DOMAIN), so *.APP_DOMAIN
# subdomains share the same login session (production only)
APP_DOMAIN=
# CLIENT_ORIGIN is the public origin of the tenant web app (scheme + host,
# no trailing slash, e.g. https://app.example.com). Used to build payment
# notify/return URLs and email verification links. Defaults to
# https://app.${APP_DOMAIN} in production and http://localhost:5173 in dev.
CLIENT_ORIGIN=
IS_OSS=true
ADMIN_EMAILS=owner@example.com
CRON_SECRET=
RSA_PRIVATE_KEY=<from step 2>
RSA_PUBLIC_KEY=<from step 2>
# Email — choose one
RESEND_API_KEY= # Resend
SMTP_HOST=
SMTP_USER=
SMTP_PASS=
SMTP_FROM=
# Optional Alibaba Cloud SMS / captcha
ALIBABA_CLOUD_ACCESS_KEY_ID=
ALIBABA_CLOUD_ACCESS_KEY_SECRET=
`ADMIN_EMAILS` accepts a comma-separated list of admin emails. During self-hosted signup, users who register with one of these emails are promoted to `admin` automatically. This is the recommended admin bootstrap path.
ALI_CAPTCHA_API_KEY=
# Optional ZPayZ payment
ZPAYZ_CID=
ZPAYZ_PID=
ZPAYZ_PAY_KEY=
ZPAYZ_GATEWAY=https://zpayz.cn/submit.php
# Optional Redis
REDIS_URL=
# Optional: OAuth
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
apps/api
Create apps/api/.env:
DATABASE_URL=postgres://user:password@localhost:5432/openproxy
RSA_PRIVATE_KEY=<same private key as server>
PORT=5060
4. Run database migrations
cd apps/server
bun run migrate
This applies the SQL in apps/server/drizzle/ to your PostgreSQL instance.
5. Start all services
# From repo root — Turborepo starts everything in parallel
bun run dev
Or start each service individually:
cd apps/api && cargo run # Rust proxy on :5060
cd apps/server && bun run dev # Bun backend on :3888
cd apps/web && bun run dev:tenant # Tenant UI on :5173
cd apps/web && bun run dev:admin # Admin UI on :5173
6. Verify the stack
# Proxy health check
curl http://localhost:5060/health
# Server health check
curl http://localhost:3888/api/health
Then open http://localhost:5173 in your browser to access the web interface.
Default ports
| Service | URL |
| --- | --- |
| Rust proxy | http://localhost:5060 |
| Bun server | http://localhost:3888 |
| Tenant web | http://localhost:5173 |
| Admin web | http://localhost:5173 |
Common startup issues
Database connection failed
Verify DATABASE_URL is correct, PostgreSQL is running, and the user has CREATE privileges (needed for migration).
Auth callback issues
For local development, BETTER_AUTH_URL should usually stay aligned with the frontend origin as http://localhost:5173/api. Trusted origins are derived from the built-in localhost rules and APP_DOMAIN, so most setups do not need an extra auth origin environment variable.
API key decryption failed / RSA error
Ensure the RSA_PRIVATE_KEY in apps/api/.env is the same key used in apps/server/.env, and that the value is complete without broken line breaks or extra spaces. Always use values from a single run of bun scripts/generateRSAKey.ts.
Port already in use
Check if another process is using :5060 or :3888. Override with PORT=xxxx in the relevant .env file.