Separate repository - Complete example SaaS app that demonstrates DomainProxy integration.
Works with DomainProxy Cloud - Get custom domains for your SaaS without infrastructure headaches.
- User authentication — Simple login system
- Multi-tenant architecture — Each user can create multiple custom domains
- DomainProxy integration — Automatic subdomain registration via API
- Host-based routing — Detect custom domains and render tenant-specific content
# 1. Get your API key from https://proxy.froste.eu/admin
# 2. Clone and configure
cd saas-demo/saas
cp .env.example .env
# Edit .env with your API key (DOMAINPROXY_API_KEY)
# 3. Install and run
npm install
npm start
# 4. Open http://localhost:3001/login# 1. First, deploy DomainProxy (see main README)
# 2. Get your API key from your instance's /admin
# 3. Configure
cp .env.example .env
# Set DOMAINPROXY_URL to your instance
# Set DOMAINPROXY_API_KEY to your key
# 4. Run
npm install
npm startIf you're having issues with platforms like EasyPanel or Railway (where Traefik blocks forwarded headers), deploy directly on a VPS for testing:
# 1. Deploy to VPS (see deployment guide below)
# 2. Set ENABLE_VPS_DEMO=true in .env
# This bypasses host header detection and shows a demo tenant page
# 3. Access your VPS IP/domain directly
# The app will show a demo page instead of requiring custom domains| Variable | Description | Default |
|---|---|---|
PORT |
Server port | 3001 |
DOMAINPROXY_URL |
DomainProxy API URL | https://proxy.froste.eu |
DOMAINPROXY_API_KEY |
Your API key | - |
SAAS_URL |
Public URL of this SaaS | http://localhost:3001 |
DEMO_EMAIL |
Demo login email | demo@example.com |
DEMO_PASSWORD |
Demo login password | demo123 |
SESSION_SECRET |
Session encryption key | - |
ENABLE_VPS_DEMO |
Enable VPS demo mode (bypasses host header checks) | false |
When a user creates a domain in the dashboard, the SaaS:
- Calls
POST /api/v1/create-tenantto register the base domain - Calls
POST /api/v1/register-subdomainto set up the proxy - Saves the tenant info to the local database
// From server.js
await domainProxyClient.createTenant(base_domain);
await domainProxyClient.registerSubdomain(subdomain, base_domain, config.saasUrl);The user adds a CNAME record pointing their subdomain to DomainProxy:
app.customer.com CNAME proxy.froste.eu
When someone visits https://app.customer.com:
- DNS resolves to DomainProxy
- DomainProxy provisions TLS certificate (first visit)
- DomainProxy proxies request to your SaaS
- Your SaaS detects the custom domain via
X-Forwarded-Hostheader - Your SaaS renders tenant-specific content
// Host detection middleware
const host = req.headers['x-forwarded-host'] || req.headers.host;
const subdomain = host.split('.')[0];
const baseDomain = host.split('.').slice(1).join('.');
// Look up tenant and render their page
const tenant = await db.get('SELECT * FROM tenants WHERE subdomain = ? AND base_domain = ?', [subdomain, baseDomain]);saas/
├── server.js # Main application
├── .env.example # Environment template
├── package.json # Dependencies
├── Dockerfile # Container config
├── data/ # SQLite database (auto-created)
└── public/ # Static files
# Build
docker build -t saas-starter .
# Run
docker run -d \
-p 3001:3001 \
-e DOMAINPROXY_URL=https://proxy.froste.eu \
-e DOMAINPROXY_API_KEY=your_key \
-e SAAS_URL=https://your-saas.com \
-v saas_data:/app/data \
saas-starterThis starter is intentionally simple. Here are some ways to extend it:
- Add real authentication — Replace plain-text passwords with bcrypt
- Add a database — Swap SQLite for PostgreSQL/MySQL
- Add a frontend framework — React, Vue, or Svelte
- Add more tenant customization — Logo, theme, custom CSS
- Add billing — Stripe integration for paid plans
- Add analytics — Track visits per tenant
The starter includes a simple DomainProxy API client:
const domainProxyClient = {
async createTenant(baseDomain) { ... },
async registerSubdomain(subdomain, baseDomain, targetUrl) { ... },
async deleteProxy(subdomain, baseDomain) { ... },
async verifyDomain(domain) { ... }
};Copy this pattern into your own application!
MIT — use this however you want.
Need help? Check out the main DomainProxy documentation.