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