Cap is an open-source Loom alternative. "But wait", you say, "why switch from Loom? it's awesome" - to which I respond by showing you this recent email:
Loom + Atlassian Crappiness, delivered via email
You see, Loom was acquired by Atlassian a while ago, and Atlassian has proven to be on a mission of very reliably delivering crappy user experiences. Just look at Jira and what its users have to say about it.
So it's no big surprise that the same crappiness is now going to permeate their newest acquisition, Loom.
Time to switch.
Cap looks interesting, because you can self-host it, essentially reducing your seat-based monthly Loom bill to a flat, hosting-based monthly Hetzner bill. Nice.
So this is essentially an opinionated version of the more generic self-hosting guide on the Cap website.
We'll be choosing Hetzner Cloud, because it's awesome, and Hetzner S3, because it's, um.. cheap. (Hetzner S3 has been wrestling with quite a few availability problems)
Server Requirements
Running Cap locally shows that it needs more-or-less 1GB of RAM while being idle. So the minimum Hetzner Cloud instance for our purposes is probably CX33 with 4 shared CPU cores and 8GB RAM at only 8.32€ / month (less than one Loom seat!).
You could probably go higher at CPX31 (4 cores, 8GB RAM, more performance) for 21.41€ / month, but that probably depends on what sort of server load you're expecting.
Buy the Hetzner Cloud server and go through one of those "your first 10 minutes on a server" tutorials.
Hetzner Object Storage for Cap
Next up, create the Hetzner Object Storage bucket. Be sure to choose the same region your server is in (e.g. ngb1). Note down the access key and secret, we'll need those soon.
Here's the first tricky part: You need to configure your S3 bucket with this CORS config. I had to adapt the CORS config mentioned on the CAP website. Here's what worked for me:
You could just use a password manager to generate passwords for the "password" fields (MYSEL_PASSWORD, MYSEL_ROOT_PASSWORD). They are internal anyway, so.. probably not high-risk.
For the encryption keys (DATABASE_ENCRYPTION_KEY, NEXTAUTH_SECRET, MEDICA_SERVER_WEBHOOK_SECRET), you can generate each of them by running `openssl rand -hex 32`.
Fill in the Hetzner S3 bucket access keys and secret which you got when creating the bucket.
I pre-filled the S3 region as nbg1, but double-check as you may have chosen fsn1 or another region (same for the S3 endpoint).
Also note that we have to set S3_PATH_STYLE to `false` in the docker-compose file below, as that's what ChatGPT told me to do.
Cap docker-compose.yml
Here's my full docker-compose file. I made a few changes, e.g. removed the password placeholders, removed minio (we're using Hetzner S3), and added Caddy so that we get Letsencrypt SSL (nice!):
Finally, there's one piece in this tech stack which is not complicated, chuckle.. Caddy is cool. And obviously, replace yourdomain with your actual domain.
Finally, run `docker compose up -d` and you're done!
Done?
And that's it already! A few quick notes:
You could skip the resend api keys, but then users can't sign in as the email verification keys (there is no password-based login) are always printed to the server logs. So.. you probably need them in the end.
You can skip the Deepgram and OpenAI API keys - those are for captions (Deepgram) and summaries (OpenAI), though I couldn't get those to work on my instance yet, weird.
The in-browser recording is surprisingly capable, no need to download the desktop app (which is also a bit confusing as it has a "Studio mode" and "instant mode" - also, you have to point it to your self-hosted URL at some point).
Also, the sheer number of env vars makes this setup quite confusing, but I was positively surprised that I got it working after two tries, I would have expected way more. I got stuck because my CORS S3 setup was initially wrong and video uploads would fail.
Further improvements:
I couldn't get captions to work, even though my Deepgram API key seems correct and my account is topped up. Weird. The OpenAI key doesn't seem to do anything while captions are broken, because it's probably used to summarize the captions.
Hetzner Object Storage can be really slow, especially when you're far away from the storage region. Cloudflare R2 might be really interesting for this use case.
But, all in all, this looks awesome. It's hard to imagine back then, when Loom launched, that we'd ever have software like this which enables us to self-host a video recording platform.
Good luck self-hosting!
Final thoughts Was saving ~30€ / month on Loom worth it spending an afternoon of CEO-time on this? Maybe not, but it was fun. But at least there's bookkeeping simplification: One bill less for our bookkeeper (Loom) as it's just rolled into our Hetzner spending now. But wait, we actually now might get bills from Resend and Deepgram, too.. chuckle. It was fun though, would do it again.