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:

DependencyWhat for
PostgreSQLall data; schema is created and migrated automatically
Redisjob queues, caching, rate limiting
At least one model API keyAnthropic, OpenAI, Gemini, or OpenRouter

Putting it on a domain adds three more:

DependencyWhat for
A domainyourdomain.com — every URL derives from it
S3-compatible object storagefile uploads — Cloudflare R2 works well
A Resend accounttransactional 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:

ProviderRegister atWhat to set
GitHub — sign-in + reposDeveloper SettingsCallback URL. GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
Google — sign-in + Gmail, Calendar, Drive, SheetsCloud ConsoleTwo 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-inNative in the iOS app; no callback to register. APPLE_CLIENT_ID
Slackapi.slack.com/appsRedirect URL, plus the events URL https://yourdomain.com/webhooks/slack/events. SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_SIGNING_SECRET
FigmaDeveloper PortalCallback URL. FIGMA_CLIENT_ID, FIGMA_CLIENT_SECRET
CanvaDeveloper PortalRedirect URL, plus the webhook URL https://yourdomain.com/webhooks/canva/default. CANVA_CLIENT_ID, CANVA_CLIENT_SECRET
XeroDeveloper PortalRedirect URI. XERO_CLIENT_ID, XERO_CLIENT_SECRET
UpworkDeveloper PortalCallback URL. UPWORK_CLIENT_ID, UPWORK_CLIENT_SECRET
Telegram@BotFatherCreate a bot, paste its token when you connect it — the webhook registers itself.
LINEDevelopers ConsoleMessaging 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:

ServiceEnvWhat it adds
TavilyTAVILY_API_KEYWeb search
Brave SearchBRAVE_SEARCH_API_KEYWeb search (alternative)
DeepgramDEEPGRAM_API_KEYSpeech-to-text
LiveKitLIVEKIT_API_KEY, LIVEKIT_API_SECRET, LIVEKIT_WS_URLReal-time voice and live video
Cloudflare StreamCLOUDFLARE_STREAM_ACCOUNT_ID, CLOUDFLARE_STREAM_API_TOKENVideo archive and playback fallback
Vapiwebhook https://yourdomain.com/webhooks/vapi/calls in the Vapi dashboardPhone calls
17TRACKwebhook URL in the API consolePackage tracking
AirtopAIRTOP_API_KEYBrowser automation
Google MapsGOOGLE_MAPS_API_KEYGeocoding and maps
BrandfetchBRANDFETCH_CLIENT_IDCompany logo lookup
Any OTLP collectorOTEL_EXPORTER_OTLP_ENDPOINTOpenTelemetry 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.