Going live: your own domain and server
This guide takes Skein from "runs on my machine" to https://app.yourstudio.com — a real URL your
team's Unity editors connect to automatically. Budget about 30 minutes.
What you'll end up with:
- One domain (e.g.
app.yourstudio.com) serving the website, dashboard, API and the realtime WebSocket, with an HTTPS certificate that renews itself. - A Unity package that already points at your server — teammates install it, click Sign in, done.
1. Get a domain
Buy a domain from any registrar (Cloudflare Registrar, Namecheap, Porkbun, Google/Squarespace…), or use
one you already own. You'll use a subdomain for Skein, e.g. app.yourstudio.com or skein.yourstudio.com.
2. Get a server
Any Linux VPS with 2 GB RAM (4 GB is comfortable) and a public IPv4 address: DigitalOcean, Hetzner, Linode/Akamai, Vultr, AWS Lightsail… Choose Ubuntu 24.04. Note the server's IP address.
On the server, install Docker and Git:
curl -fsSL https://get.docker.com | sh
apt-get install -y git
Open the firewall for web traffic (if your provider has one, open ports 80 and 443 there too):
ufw allow OpenSSH && ufw allow 80 && ufw allow 443 && ufw --force enable
3. Point the domain at the server
In your DNS provider, add a record:
| Type | Name | Value | TTL |
|---|---|---|---|
A |
app (for app.yourstudio.com) |
your server's IP | Auto / 300 |
If you use Cloudflare DNS, set the record to DNS only (grey cloud) for the first setup so the certificate can be issued; you can turn the proxy on afterwards (WebSockets work through Cloudflare).
Check it resolves (may take a few minutes):
dig +short app.yourstudio.com # should print your server's IP
4. Put Skein on the server
Copy the repository to the server (Git remote, or scp -r skein root@SERVER_IP:/opt/skein), then:
cd /opt/skein
tools/setup-production.sh app.yourstudio.com you@yourstudio.com
This writes infrastructure/production/.env with freshly generated secrets (database password, token
signing keys). Keep that file private and backed up — it is git-ignored.
5. Start it
docker compose -f infrastructure/production/docker-compose.prod.yml --profile https up -d --build
The first build takes a few minutes. Then:
docker compose -f infrastructure/production/docker-compose.prod.yml ps # all "healthy"/"running"
curl -s https://app.yourstudio.com/api/readyz # {"ok":true,...}
Open https://app.yourstudio.com — create your account. That's your live Skein.
Database migrations run automatically on every start; the HTTPS certificate is obtained from Let's Encrypt on first request and renewed automatically.
6. Make the Unity package use your server automatically
Pick one (or both):
a) Bake the URL into the package you distribute (best for selling / sharing the package):
tools/set-server-url.sh https://app.yourstudio.com
Every copy of the package built from the repo after this connects to your server out of the box.
b) Per Unity project, shared through version control: in Unity open Tools → Skein → Collaborate →
Server, enter app.yourstudio.com, click Save for whole team, and commit the created
ProjectSettings/SkeinSettings.json. Everyone who pulls the project uses your server — no setup.
Precedence: a person's own override → the project's SkeinSettings.json → the URL built into the package.
7. Email (recommended)
Without email, Skein still works: new accounts are verified automatically (REQUIRE_EMAIL_VERIFICATION=false)
and the dashboard shows a Copy invite link after you invite someone. To send real emails (verification,
password resets, invitations), create an SMTP account (Resend, Postmark, Amazon SES, Mailgun…) and set in
infrastructure/production/.env:
SMTP_URL=smtps://USERNAME:PASSWORD@smtp.provider.com:465
MAIL_FROM=Skein <no-reply@yourstudio.com>
REQUIRE_EMAIL_VERIFICATION=true
Then restart: docker compose -f infrastructure/production/docker-compose.prod.yml --profile https up -d.
8. Backups
crontab -e
# add:
0 3 * * * /opt/skein/tools/backup.sh >> /var/log/skein-backup.log 2>&1
Backups (database dump + snapshot files, 14 days) go to /opt/skein/backups. Copy them off the server
too (e.g. rclone to S3/B2). Restore a database dump with
docker compose … exec -T postgres pg_restore -U skein -d skein --clean < backups/skein-db-….dump.
9. Updating
cd /opt/skein && git pull
docker compose -f infrastructure/production/docker-compose.prod.yml --profile https up -d --build
Editors reconnect automatically during the restart and keep their locks.
10. Selling plans (optional)
Create products/prices in Stripe, then set BILLING_MODE=stripe, STRIPE_SECRET_KEY,
STRIPE_WEBHOOK_SECRET, the STRIPE_PRICE_* ids and DEFAULT_PLAN=free in .env, and add a Stripe
webhook to https://app.yourstudio.com/api/v1/billing/webhook (events checkout.session.completed and
customer.subscription.*). Prices shown on the site come from packages/shared/src/plans.ts.
Alternative: a server that already runs a website on ports 80/443
If your server already has a reverse proxy (Caddy, nginx, Traefik) for other sites, don't start
Skein's Caddy. Start the stack without the https profile and bind it to the address your proxy can reach:
# in infrastructure/production/.env — for a proxy running in Docker on the same host:
WEB_BIND=172.17.0.1:3201
REALTIME_BIND=172.17.0.1:4002
docker compose -f infrastructure/production/docker-compose.prod.yml up -d --build
Then add one site to your existing proxy. For Caddy:
app.yourstudio.com {
encode zstd gzip
@realtime path /v1/ws
reverse_proxy @realtime 172.17.0.1:4002
reverse_proxy 172.17.0.1:3201
}
For nginx, proxy /v1/ws to port 4002 with proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 3600s; and everything else to port 3201.
Reload the proxy, and continue with step 6.
Troubleshooting
| Symptom | Fix |
|---|---|
| Browser shows a certificate error | DNS not pointing at the server yet, or ports 80/443 closed. docker compose … logs caddy. |
| Unity: "Can't reach the collaboration server" | Check https://app.yourstudio.com/api/readyz; make sure nothing in front of the server blocks WebSockets on /v1/ws. |
| Sign-in page says the code expired | Codes last 10 minutes; start sign-in again from Unity. |
| Logs | docker compose -f infrastructure/production/docker-compose.prod.yml logs -f api realtime web |