Viewer guide

Where to run it

The Viewer ships as a container. Hand it to a host that runs containers, attach a disk, and you are done. This page names the hosts that work, the settings to fill in, and the ones that cannot run it.

The Viewer ships as a container image, so you do not have to build anything or set up a server. Pick a host from the table below, give it the image, attach a disk, and you get an address with https on it.

It needs two things from wherever it runs, and they are the only reasons some hosts are missing from that table. It has to keep its disk, because everything it saves lives in one folder. And it has to stay awake, because it builds prototypes and runs them for reviewers while they look.

Hosts that take the container#

Host What it calls the disk Guides
Fly.io Volume Deploy an image, Volumes
Render Disk Deploy an image, Disks
Railway Volume Services, Volumes

All three run a container from an image, give you a disk that survives a restart, put https on a domain you own, and can serve each prototype on its own address. Pick whichever your team already uses. If you use none of them, Fly.io and Render are the two with the least to fill in.

What to fill in#

Wherever you run it, these are the same.

Image ghcr.io/desde-design/viewer:latest
Port 3100
Disk 40 GB, mounted at /data
Size 2 CPUs and 4 GB of memory
Environment The values listed under Run the container

One more setting, whatever the host calls it: turn off sleeping when idle, or scaling to zero. The Viewer has to stay up. Fly.io calls this auto stop, and Render and Railway keep a paid service running by default.

Note These hosts give you an address on the public internet. Sign-in is invite-only, so an address alone gets nobody in. If your prototypes must never leave your own network, run it on a virtual machine instead, which is the next section.

Then go to Deploy the Viewer for the environment values, and the GitHub App for sign-in.

On a virtual machine#

Docker on any Linux virtual machine runs the same image. This is the way to go if the Viewer has to sit inside your own network, or you already have a server for internal tools.

Any of these is fine: a Droplet at DigitalOcean, a Cloud Server at Hetzner, a Compute Instance at Akamai, Lightsail or EC2 at Amazon, a Compute Engine instance at Google, or a Virtual Machine at Azure. Same size as above. Install Docker, then follow Deploy the Viewer.

What cannot run it#

Google Cloud Run, and the ones shaped like it. AWS App Runner and Lambda, Azure Container Apps and Heroku belong here too. They start a container to answer a request and stop it afterwards, and the next request may get a different one. Two things break at once. Everything the Viewer saved is gone when the container goes, and a stopped container cannot build a prototype or run one for someone reviewing it. Cloud Run can now mount cloud storage as a folder, which looks like the missing piece but is not: that mount has no file locking, and the Viewer's database needs a real disk.

Vercel, Netlify and GitHub Pages. They serve a website that is already built. The Viewer is what does the building, so it needs somewhere to run, not somewhere to publish.

For engineers#

  • The disk has to be a real filesystem. State is SQLite in WAL mode plus the published assets, both under /data. A block volume is fine. A FUSE bucket mount or an NFS share is not, which is the Cloud Run point above.
  • Builds are the load and they are untrusted. Connecting a repository means running that repository's own install and build commands in this container. Two may run at once, ten minutes to a step. That is what the 4 GB is for.
  • Long-lived connections and child processes. Build logs and comment updates are server-sent events. A prototype whose framework needs a server is run as a child process, up to four at once, each stopped after half an hour idle. Add memory here if anywhere.
  • The image is multi-arch, so ARM instances are fine. Each build keeps up to 200 MB of output and nothing prunes old ones: see backups and disk space.
  • One instance, never two. One process, one folder, no clustering. Every host above disables horizontal scaling once a disk is attached, which is the behavior you want.
  • A wildcard domain gives each prototype its own address, which is the recommended setup: see where prototypes are served. Fly.io, Render and Railway all issue wildcard certificates. On a virtual machine the certificate goes on the proxy in front, see HTTPS and a reverse proxy, with Certbot's DNS plugins for the wildcard.
  • Running from a checkout instead needs Node 24 or newer: see run without Docker.

The sizes here are a starting point, not a measurement. The Viewer has not been run for an outside team yet.