Self-Hosting Guide
Daslab Cloud is the fast path, but nothing about Daslab requires our servers. The whole thing ships as a single binary: point it at a Postgres and a Redis, give it a domain, and you have a Daslab that runs entirely on your infrastructure. This guide covers the software part, which is an afternoon, and then the accounts — the reason most teams stay on Cloud.
The server is one binary
One process serves everything: the API, the website, sign-in, webhooks, and background job processing. There is no cluster to operate and no fleet of services to keep in sync.
Ask us for the current build for your platform. Everything else you need is on this page.
What it needs
Three things boot a working instance:
| Dependency | What for |
|---|---|
| PostgreSQL | all data; schema is created and migrated automatically |
| Redis | job queues, caching, rate limiting |
| At least one model API key | Anthropic, OpenAI, Gemini, or OpenRouter |
Putting it on a domain adds three more:
| Dependency | What for |
|---|---|
| A domain | yourdomain.com — every URL derives from it |
| S3-compatible object storage | file uploads — Cloudflare R2 works well |
| A Resend account | transactional email: magic links, invites |
Any managed flavor of Postgres and Redis works — the server only needs a connection URL for each.
Start the server
The essentials, then run the binary:
DATABASE_URL=postgresql://user:pass@host:5432/daslab
UPSTASH_REDIS_URL=rediss://default:xxx@host:6379 # any Redis URL; the name is historical
# Models — at least one
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
GEMINI_API_KEY=...
OPENROUTER_API_KEY=sk-or-...
On first boot the server creates and migrates its database schema itself — there is no separate migration step, now or on any later upgrade.
Going public adds the second tier:
# The one variable every URL derives from — OAuth callbacks, links, sitemaps
PUBLIC_BASE_URL=https://yourdomain.com
# File uploads (any S3-compatible store — R2 shown)
R2_ACCESS_KEY_ID=your_access_key
R2_SECRET_ACCESS_KEY=your_secret_key
R2_ACCOUNT_ID=your_account_id
R2_USER_BUCKET_NAME=daslab-user
R2_PUBLIC_URL=https://tmp.yourdomain.com
# Email
RESEND_API_KEY=re_...
FROM_EMAIL=Daslab <noreply@yourdomain.com>
CONTACT_EMAIL=hello@yourdomain.com
Open https://yourdomain.com and you're looking at your own instance. Magic-link sign-in needs nothing beyond Resend.
File uploads need one DNS record
Create your uploads bucket, add tmp.yourdomain.com as its custom domain (R2 manages the DNS record and TLS for you), and set R2_PUBLIC_URL to match. Uploaded files are now served from your own subdomain.
Verify your email domain
In the Resend dashboard, add your domain and set the DNS records it asks for (SPF, DKIM, DMARC). Once verified, magic links and invites go out from your FROM_EMAIL address.
Some integrations need an app in your name
Most of the catalog needs nothing from your server at all: API-key integrations ask for their key in the app when you connect them, and MCP servers connect the same way. A few messaging providers just need a webhook URL pasted into their console. The rest are OAuth — those need a registered app and callbacks pointing at your domain.
The pattern is the same for every OAuth provider: register an app in its developer portal, set {PROVIDER}_CLIENT_ID and {PROVIDER}_CLIENT_SECRET, and give it the callback
https://yourdomain.com/auth/{provider}/callback
The common registrations:
| Provider | Register at | What to set |
|---|---|---|
| GitHub — sign-in + repos | Developer Settings | Callback URL. GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET |
| Google — sign-in + Gmail, Calendar, Drive, Sheets | Cloud Console | Two redirect URIs: /auth/google/callback and /auth/google/signin/callback. Consent screen required; production use needs Google's verification. GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET |
| Apple — sign-in | — | Native in the iOS app; no callback to register. APPLE_CLIENT_ID |
| Slack | api.slack.com/apps | Redirect URL, plus the events URL https://yourdomain.com/webhooks/slack/events. SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_SIGNING_SECRET |
| Figma | Developer Portal | Callback URL. FIGMA_CLIENT_ID, FIGMA_CLIENT_SECRET |
| Canva | Developer Portal | Redirect URL, plus the webhook URL https://yourdomain.com/webhooks/canva/default. CANVA_CLIENT_ID, CANVA_CLIENT_SECRET |
| Xero | Developer Portal | Redirect URI. XERO_CLIENT_ID, XERO_CLIENT_SECRET |
| Upwork | Developer Portal | Callback URL. UPWORK_CLIENT_ID, UPWORK_CLIENT_SECRET |
| Telegram | @BotFather | Create a bot, paste its token when you connect it — the webhook registers itself. |
| LINE | Developers Console | Messaging API channel; webhook URL https://yourdomain.com/webhooks/line/bot. LINE_CHANNEL_ID, LINE_CHANNEL_SECRET |
This table isn't the full catalog — the integrations page is. Every OAuth provider there follows the same two-variables-and-a-callback pattern, and one you haven't registered simply won't connect until you do. You only need the ones you'll actually use — a GitHub-only instance is a perfectly good instance. But each app you do register becomes yours to own: client IDs, consent screens, keys to rotate. On Cloud, all of this already exists and is maintained for you. That's the trade.
Point the apps at your server
In the iOS app, open Profile → Server Accounts → Add Server, enter your URL, and sign in. You can stay connected to Daslab Cloud and your own instance at the same time — workspaces from both appear in one list, and every call routes to the right server automatically.
One honest caveat: push notifications for the App Store build are signed with our Apple developer key, so a self-hosted server can't send them on its own. Reach out if you need push.
For the CLI:
export DASLAB_API_URL=https://yourdomain.com
Optional services
Capabilities switch on when their key is present and off when it isn't — nothing else to configure, and the same rule holds for keys not listed here. The ones you're most likely to want:
| Service | Env | What it adds |
|---|---|---|
| Tavily | TAVILY_API_KEY | Web search |
| Brave Search | BRAVE_SEARCH_API_KEY | Web search (alternative) |
| Deepgram | DEEPGRAM_API_KEY | Speech-to-text |
| LiveKit | LIVEKIT_API_KEY, LIVEKIT_API_SECRET, LIVEKIT_WS_URL | Real-time voice and live video |
| Cloudflare Stream | CLOUDFLARE_STREAM_ACCOUNT_ID, CLOUDFLARE_STREAM_API_TOKEN | Video archive and playback fallback |
| Vapi | webhook https://yourdomain.com/webhooks/vapi/calls in the Vapi dashboard | Phone calls |
| 17TRACK | webhook URL in the API console | Package tracking |
| Airtop | AIRTOP_API_KEY | Browser automation |
| Google Maps | GOOGLE_MAPS_API_KEY | Geocoding and maps |
| Brandfetch | BRANDFETCH_CLIENT_ID | Company logo lookup |
| Any OTLP collector | OTEL_EXPORTER_OTLP_ENDPOINT | OpenTelemetry trace export |
Upgrading
Swap the binary and restart. The server is stateless — all state lives in Postgres, Redis, and your object store — and migrations run themselves on boot.
To move domains without breaking sign-in: add the new callback URLs at every provider alongside the old ones, deploy with the new PUBLIC_BASE_URL, verify each flow, then remove the old URLs.
When something doesn't work
OAuth redirects fail
Almost always a mismatch between PUBLIC_BASE_URL and the callback URLs registered at the provider. Compare them character by character — a trailing slash is enough.
File uploads 404
The uploads custom domain hasn't propagated. curl -I https://tmp.yourdomain.com should return a response, not a DNS error.
Emails don't arrive
Your domain isn't fully verified in Resend — all three DNS records (SPF, DKIM, DMARC) need to be in place.
Is it even up?
curl https://yourdomain.com/health returns status, version, and build.
Cloud is the same software, operated for you
A self-hosted instance is yours to run end to end: the OAuth apps and their consent screens, the client secrets and their rotation, the email domain and its DNS records, the binary and its upgrades. On Cloud those are our job. Your data and the trace of every run are yours on either path.
Self-hosted deployments are part of the Enterprise plan — the build, deployment guidance, and support are included. Get a demo.