Run Your Own Instance
Get Made Open running as a production system on your own infrastructure. This guide is for anyone who wants to operate the platform — not develop it. No coding required.
What You're Setting Up
Made Open runs as six containers on a single server, backed by a managed database:
Internet
|
[ Traefik ] ← reverse proxy, auto-HTTPS
/ \
[ Hub API ] [ Web App ] ← your backend + frontend
| |
[ NATS ] [ Redis ] ← event bus + cache
|
[ Meilisearch ] ← full-text search
|
────── Supabase Cloud ────── ← database, auth, file storage
| Container | What It Does |
|---|---|
| Hub | The backend — runs all services, processes events, talks to integrations |
| Web | The browser UI — dashboard, settings, conversations, rules |
| NATS | Message broker — services communicate through events, not direct calls |
| Redis | Cache layer — speeds up frequent lookups, rate limiting |
| Meilisearch | Search engine — instant, typo-tolerant search across all your data |
| Traefik | Reverse proxy — routes traffic to Hub and Web, handles HTTPS certificates automatically |
Supabase Cloud runs externally as a managed service. It provides the PostgreSQL database, user authentication, encrypted credential storage (Vault), file storage, and real-time subscriptions.
What You Need
Three things before you start:
| What | Why | How Long |
|---|---|---|
| A server with Docker | Runs the six containers | 5 minutes to provision |
| A Supabase Cloud account | Managed database + auth | 5 minutes to create |
| A domain name | For HTTPS and clean URLs | Already have one, or 2 minutes to buy |
Total setup time: ~30 minutes.
Step 1: Create a Supabase Project
Supabase is the database and authentication layer. You need a cloud project.
- Go to supabase.com/dashboard and create an account
- Click New Project
- Choose a name (e.g.,
made-open) and set a strong database password - Select a region close to your server
- Wait for the project to provision (~2 minutes)
Once ready, go to Settings > API and copy these four values — you'll need them in Step 4:
| Value | Where to Find It | What It's For |
|---|---|---|
| Project URL | Settings > API > Project URL | SUPABASE_URL — how the hub connects to the database |
| anon public key | Settings > API > Project API keys | SUPABASE_ANON_KEY — safe for browser use, respects row-level security |
| service_role key | Settings > API > Project API keys | SUPABASE_SERVICE_ROLE_KEY — full database access, keep this secret |
| Connection string | Settings > Database > Connection string (URI) | DATABASE_URL — direct PostgreSQL connection |
Apply Database Migrations
You need to run the schema migrations once. Install the Supabase CLI on any computer:
# Install Supabase CLI
npm install -g supabase
# Log in to your account
supabase login
# Clone the repo (just for migrations — you won't run code from it)
git clone https://github.com/drdropout/made-open.git
cd made-open
# Link to your cloud project
supabase link --project-ref <your-project-ref>
# Push all 53 migrations
supabase db push
The <your-project-ref> is the alphanumeric ID in your Supabase project URL (e.g., abcdefghijklmnop from https://abcdefghijklmnop.supabase.co).
After this, you can delete the cloned repo if you like — the migrations are in the database now.
Supabase Pricing
| Tier | Cost | What You Get |
|---|---|---|
| Free | $0/mo | 500 MB database, 1 GB storage, 50k monthly active users, shared compute |
| Pro | $25/mo | 8 GB database, 100 GB storage, unlimited users, daily backups, dedicated compute |
The Free tier works fine for personal use. Upgrade to Pro when you need backups or more storage.
Step 2: Provision a Server
You need a Linux server with Docker installed. Any cloud provider works — here are three popular options:
Option A: Hetzner (Best Value)
- Go to hetzner.com/cloud and create an account
- Create a new server:
- Location: Choose closest to you
- Image: Ubuntu 24.04
- Type: CX22 (2 vCPU, 4 GB RAM) — $4.50/mo
- SSH key: Add your public key (or create one)
- Note the server's IP address
# SSH into your new server
ssh root@<your-server-ip>
# Install Docker
curl -fsSL https://get.docker.com | sh
Option B: DigitalOcean
- Go to digitalocean.com and create an account
- Create a Droplet:
- Image: Ubuntu 24.04
- Size: Basic, 2 vCPU / 4 GB RAM — $24/mo
- Region: Choose closest to you
- Authentication: SSH key
- Note the Droplet's IP address
ssh root@<your-droplet-ip>
curl -fsSL https://get.docker.com | sh
Option C: AWS Lightsail
- Go to lightsail.aws.amazon.com and sign in
- Create an instance:
- Platform: Linux/Unix
- Blueprint: Ubuntu 24.04
- Plan: 2 GB RAM — $10/mo (or 4 GB — $20/mo)
- Note the static IP address
ssh ubuntu@<your-lightsail-ip>
curl -fsSL https://get.docker.com | sh
Server Sizing Reference
| Server Size | Handles | Cost Range |
|---|---|---|
| 2 vCPU / 4 GB RAM | Personal use, 1-5 users | $4-24/mo |
| 4 vCPU / 8 GB RAM | Small team, heavier AI usage | $10-48/mo |
| 8 vCPU / 16 GB RAM | Power user with many integrations | $20-96/mo |
Step 3: Point Your Domain
Create two DNS A records pointing to your server's IP address. Do this wherever you manage your domain (Cloudflare, Namecheap, Porkbun, Route 53, etc.).
| Record Type | Name | Value |
|---|---|---|
| A | api.yourdomain.com | <your-server-ip> |
| A | app.yourdomain.com | <your-server-ip> |
Examples by registrar:
- Cloudflare: Dashboard > your domain > DNS > Add record. Set proxy status to "DNS only" (grey cloud) so Traefik handles TLS.
- Namecheap: Domain List > Manage > Advanced DNS > Add New Record.
- Porkbun: DNS Records > Add. Type: A, Host:
apiorapp, Answer: your server IP.
DNS propagation usually takes 1-5 minutes, but can take up to an hour.
Step 4: Configure Your Server
SSH into your server and set up the deployment:
ssh root@<your-server-ip>
# Create a directory for Made Open
mkdir -p /opt/made-open && cd /opt/made-open
# Download the production compose file and env template
curl -O https://raw.githubusercontent.com/drdropout/made-open/main/docker-compose.prod.yml
curl -O https://raw.githubusercontent.com/drdropout/made-open/main/.env.prod.example
# Copy the template to .env
cp .env.prod.example .env
Now edit .env and fill in every value:
nano .env # or vim, or any editor you prefer
Here's what each variable means:
# ── Your domains (from Step 3) ──
HUB_DOMAIN=api.yourdomain.com # Where the API lives
WEB_DOMAIN=app.yourdomain.com # Where the web UI lives
ACME_EMAIL=you@example.com # Let's Encrypt sends expiry warnings here
# ── Image version ──
TAG=latest # Docker image tag — use 'latest' or a specific version like 'v1.2.0'
# ── Supabase keys (from Step 1) ──
SUPABASE_URL=https://abcdefgh.supabase.co
SUPABASE_SERVICE_ROLE_KEY=eyJ... # The service_role key — KEEP THIS SECRET
SUPABASE_ANON_KEY=eyJ... # The anon key — safe for browsers
DATABASE_URL=postgresql://postgres:your-password@db.abcdefgh.supabase.co:5432/postgres
NEXT_PUBLIC_SUPABASE_URL=https://abcdefgh.supabase.co # Same as SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ... # Same as SUPABASE_ANON_KEY
# ── Security — generate unique random strings ──
JWT_SECRET= # Run: openssl rand -hex 32
MEILI_MASTER_KEY= # Run: openssl rand -hex 16
CORS_ORIGINS=https://app.yourdomain.com
NODE_ENV=production
# ── Optional (uncomment if you want these features) ──
# OPENROUTER_API_KEY=sk-or-... # Cloud AI — get a key at openrouter.ai
# STRIPE_SECRET_KEY=sk_live_... # Billing — from stripe.com dashboard
# STRIPE_WEBHOOK_SECRET=whsec_... # Billing — from Stripe webhook settings
Generate the security secrets:
# Generate and paste these into your .env
openssl rand -hex 32 # → JWT_SECRET
openssl rand -hex 16 # → MEILI_MASTER_KEY
Step 5: Start Everything
cd /opt/made-open
# Pull the container images
docker compose -f docker-compose.prod.yml pull
# Start all services
docker compose -f docker-compose.prod.yml up -d
Watch the logs during first startup:
docker compose -f docker-compose.prod.yml logs -f
Traefik will automatically obtain HTTPS certificates from Let's Encrypt on the first request. This takes 30-60 seconds.
Step 6: Verify It's Working
# Check all containers are running
docker compose -f docker-compose.prod.yml ps
# Hub health check
curl https://api.yourdomain.com/health
# Expected: {"status":"ok","version":"0.0.1"}
# Detailed health (shows status of each internal service)
curl https://api.yourdomain.com/health/detailed
# Web app
curl -I https://app.yourdomain.com
# Expected: HTTP/2 200
Open https://app.yourdomain.com in your browser. You should see the Made Open login page.
Step 7: Create Your Account
- Open
https://app.yourdomain.comin your browser - Click Sign Up
- Enter your email and password
- Confirm your email (Supabase sends a verification link)
- Log in — you'll land on the dashboard
Step 8: Add Integrations
Integrations activate dynamically when you add credentials through Settings. Nothing is required — add what you need.
Voice and SMS (Twilio)
- Create a Twilio account at twilio.com
- Buy a phone number (~$1/month) — ensure it's voice and SMS capable
- Copy your Account SID and Auth Token from the Twilio Console
- In Made Open: Settings > Twilio > Connect
- Enter Account SID, Auth Token, and Phone Number
- Voice Calls and SMS Messaging appear in the dashboard
Email, Calendar, Contacts (Microsoft 365)
Requires a one-time Azure AD app registration. Full walkthrough: credential-setup.md
Quick version:
- Register an app at portal.azure.com > Azure Active Directory > App registrations
- Set redirect URI to
https://api.yourdomain.com/api/oauth/microsoft/callback - Add delegated permissions:
User.Read,Contacts.Read,Calendars.Read,Mail.Read,Files.Read,Tasks.Read - Create a client secret
- Add
MICROSOFT_CLIENT_ID,MICROSOFT_CLIENT_SECRET,MICROSOFT_REDIRECT_URI,MICROSOFT_TENANT_IDto your.env - Restart the hub:
docker compose -f docker-compose.prod.yml restart hub - In Made Open: Settings > Microsoft 365 > Connect — sign in with your Microsoft account
Email, Calendar, Contacts (Google Workspace)
Requires a Google Cloud project with OAuth. Full walkthrough: credential-setup.md
Quick version:
- Create a project at console.cloud.google.com
- Enable Gmail, Calendar, People, and Drive APIs
- Create OAuth 2.0 credentials with redirect URI
https://api.yourdomain.com/api/oauth/google/callback - Add client ID and secret to your
.env - Restart the hub
- In Made Open: Settings > Google Workspace > Connect
Cloud AI (OpenRouter)
- Create an account at openrouter.ai
- Go to Keys > Create Key
- Copy the API key (starts with
sk-or-v1-) - In Made Open: Settings > OpenRouter > Connect, paste the key
- AI features activate — assistant, summaries, smart replies
Pay-per-token pricing. Most queries cost fractions of a cent.
Local AI (Ollama)
Run AI models locally on your server — no API key, no cost, full privacy.
- Install Ollama on your server:
curl -fsSL https://ollama.com/install.sh | sh - Pull a model:
ollama pull llama3.2 - In Made Open: Settings > Ollama > Connect
- Enter the URL (default:
http://localhost:11434)
Works best with a GPU. CPU-only is possible but slower.
Optional Add-Ons Reference
| Integration | What It Enables | Your Own Account? | Approximate Cost |
|---|---|---|---|
| Twilio | Voice calls, SMS, voicemail, video | Yes | ~$1/mo + $0.01/SMS, $0.013/min voice |
| Microsoft 365 | Email, calendar, contacts, OneDrive, Tasks | Yes (Azure AD) | Included with M365 subscription |
| Google Workspace | Gmail, calendar, contacts, Drive, Meet | Yes (Google Cloud) | Free with Google account |
| OpenRouter | Cloud AI (Claude, GPT-4, Gemini, etc.) | Yes | Pay-per-token (~$0.001-0.01 per query) |
| Ollama | Local AI inference (Llama, Qwen, etc.) | No | Free (self-hosted) |
| Stripe | Billing and subscriptions | Yes | 2.9% + $0.30 per transaction |
| LightRAG | Knowledge graph + entity extraction | No | Free (add container to docker-compose) |
| Firebase FCM | Push notifications to mobile | Yes (Firebase) | Free tier is generous |
What It Costs
Minimal (~$5/month)
For personal use — just you and basic features.
| Item | Provider | Cost |
|---|---|---|
| Server | Hetzner CX22 (2 vCPU, 4 GB) | $4.50/mo |
| Database | Supabase Free tier | $0/mo |
| Domain | Already own one | $0/mo |
| Total | ~$5/mo |
Recommended (~$30/month)
For daily use with backups and headroom.
| Item | Provider | Cost |
|---|---|---|
| Server | DigitalOcean 4 GB Droplet | $24/mo |
| Database | Supabase Pro | $25/mo |
| Domain | Any registrar | ~$1/mo |
| Total | ~$50/mo |
Full-Featured (~$55+/month)
Everything above plus communications and AI.
| Item | Provider | Cost |
|---|---|---|
| Server + Database | (same as Recommended) | $50/mo |
| Twilio | Phone number + usage | ~$2-10/mo |
| OpenRouter | AI queries | ~$1-10/mo |
| Total | ~$55-70/mo |
Maintenance
Updating
When a new version is released, update with two commands:
cd /opt/made-open
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
To pin a specific version:
TAG=v1.2.0 docker compose -f docker-compose.prod.yml up -d
Backups
| Data | Where It Lives | Backup Strategy |
|---|---|---|
| Users, entities, messages, rules | Supabase Cloud | Automatic daily backups (Pro tier). Manual backups via Dashboard. |
| NATS event history | nats_data Docker volume | Use docker run --rm -v nats_data:/data -v /backups:/backup alpine tar czf /backup/nats.tar.gz /data |
| Search indexes | meili_data Docker volume | Rebuilds from database on startup — backup optional |
| Redis cache | redis_data Docker volume | Ephemeral — no backup needed |
| TLS certificates | letsencrypt Docker volume | Auto-renews — backup optional |
Monitoring
The hub exposes monitoring endpoints:
| Endpoint | What It Shows |
|---|---|
GET /health | Simple OK/not-OK — use as a liveness probe |
GET /health/detailed | Status of every internal service (NATS, Meilisearch, database) |
GET /metrics | Prometheus-compatible metrics — connect Grafana for dashboards |
View live logs:
docker compose -f docker-compose.prod.yml logs -f hub
The hub outputs structured JSON logs via pino. Route container stdout to any log aggregator (Datadog, Loki, CloudWatch, etc.).
Troubleshooting
| Problem | Check |
|---|---|
| Containers won't start | docker compose -f docker-compose.prod.yml logs — look for missing env vars or connection errors |
| HTTPS not working | Ensure DNS A records point to the server IP and ports 80/443 are open. Check docker compose -f docker-compose.prod.yml logs traefik |
| "Connection refused" from hub | Check that NATS, Meilisearch, and Redis containers are running: docker compose -f docker-compose.prod.yml ps |
| Supabase connection fails | Verify DATABASE_URL includes the correct password and hostname. Test with psql $DATABASE_URL |
| Login page shows but can't sign up | Check SUPABASE_URL and SUPABASE_ANON_KEY match your Supabase project. Ensure email confirmations are configured in Supabase Dashboard > Auth > Settings |
| Integrations don't appear after adding credentials | Check hub logs for errors. Restart the hub: docker compose -f docker-compose.prod.yml restart hub |
Further Reading
- Deployment Guide — Full environment variable reference and platform-specific options (Railway, Fly.io, Vercel)
- Docker Compose Deployment — Detailed self-hosted Docker Compose reference
- Credential Setup — Step-by-step for every integration
- Monitoring Guide — Prometheus, Grafana, and Loki setup
- Backup and Recovery — Comprehensive backup strategies
- Troubleshooting — Extended troubleshooting reference