#Introduction
Bay is a self-hosted application server for Alepha apps.
Where Cloudflare gives you someone else's serverless platform, Bay runs your apps as ordinary long-lived processes on a machine you own, with TLS, rollback and process isolation handled for you.
#When to use it
- You want a long-lived runtime: background work between requests, in-process caches, a local SQLite file.
- You would rather pay for one VPS than per request.
- The data has to stay on hardware you control.
#Deploying to it
Bay has no wrangler.jsonc equivalent. Everything it needs is already in the build
manifest, so the target-specific story is one line of configuration:
1platform({2 environments: {3 production: bay({4 host: "deploy@bay.example.com",5 domain: "myapp.com",6 }),7 },8});
Then alepha platform up -e production.
One more field matters on a non-default install: if Bay's root is not
$HOME/bay-data, set socket to the absolute path of the control socket -
without it, every command the adapter sends fails to find the server.
#Static files and their headers
Bay answers every file it finds under the release's dist/public itself, without waking
the app: a page pulls tens of them. So the app's own headers never reach those files, and
Bay applies dist/public/_headers to them instead, the file alepha build writes and
Cloudflare applies to the same build:
- the security headers
helmetOptionsproduces, on every file; - a year of immutable caching on content-hashed chunks and assets;
- your own rules from
public/_headers; public, max-age=0, must-revalidatefor a file no rule caches.
The rules match the path the browser asked for, percent-encoded, never the file Bay found:
/changelog served from changelog.html matches /changelog, and a miss answered with
404.html matches the missed path. A request the app answers is left alone, as on
Cloudflare; the app applies the same file to what its own server serves.
A release whose _headers does not parse is refused at deploy, naming the line, and the
running release keeps serving. /_headers, /_redirects and /.assetsignore are never
served. A release built before _headers existed is served as it always was. See
Static File Headers for the file itself.
#HTTPS
Started with --tls, Bay obtains a certificate for every app's domain and
answers plain HTTP on port 80 with a 308 to the same address over HTTPS, so a
visitor who types the address or follows an http:// link never gets the site
unencrypted. Port 80 still answers the ACME HTTP-01 challenge first, and a host
no app is registered for keeps its 404.
⚠️ If a Cloudflare proxy sits in front of Bay, set its SSL mode to Full (strict), never Flexible. Flexible talks plain HTTP to the origin, so every request is redirected back to HTTPS and the browser reports "too many redirects", with nothing in Bay's logs to say why.
#Installing Bay
Bay is written in Go and ships as a static Linux binary on every release, for
amd64 and arm64, alongside a SHA256SUMS file. Download the binary for your
architecture from the GitHub releases,
verify the checksum, and run it. The releases also expose stable
releases/latest/download/bay-linux-<arch> URLs for scripted installs.