Deploy on a server
Set up DNS, run the one-command installer and log in on any Linux server with Docker.
This is the generic install. For a guided setup, see Platforms or Cloud VMs.
Prerequisites
| Requirement | Value |
|---|---|
| Server | Linux with Docker and Docker Compose v2, ports 80 and 443 open |
| Size | 4 vCPU, 8 GB RAM, 80 GB SSD for up to 100,000 users. See Sizing. |
| Domain | One domain you control, with DNS you can edit |
DNS
Grovs uses up to three domains. The installer asks for them in this order. Press Enter to accept each default.
| Prompt | Variable | What lives there | Default |
|---|---|---|---|
| App domain | SERVER_HOST | dashboard., api., sdk., go., mcp., preview. | local trial on lvh.me |
| Links domain | DOMAIN_LIVE | production links: <project>.<links domain> | the app domain |
| Test links domain | DOMAIN_TEST | test links: <project>.<test links domain> | test.<links domain> |
The usual setup is a branded short domain for links (acme.link) and the company domain for the app (acme.com). One domain for everything also works. The test links domain can be a sub-label of the links domain (the default) or a separate domain.
Create every record as an A record pointing at your server's IPv4. Add a matching AAAA if the server has IPv6.
| Domain | Name (host) | Serves |
|---|---|---|
| app | dashboard, api, sdk, go, mcp, preview | the fixed hosts |
| links | * (wildcard) | per-project production links, for example a1b2c3d4.acme.link |
| links | links | LINKS_PROD_HOST, with a pre-issued certificate |
| test links | * (wildcard) | per-project test links, for example a1b2c3d4.test.acme.link |
| test links | links | LINKS_TEST_HOST, with a pre-issued certificate |
With one domain for everything, that is the six fixed hosts plus * and *.test.
The * wildcards are mandatory. Every project gets its own random link subdomain. Without the wildcards those subdomains return 404 and cannot get a TLS certificate.
TLS and Universal Links
The standalone Caddy proxy issues certificates on demand for each new subdomain. The first request takes a few seconds. Apple's and Google's association fetchers can time out on that cold start and cache the failure for about an hour.
For reliable Universal Links and App Links, pre-issue wildcard certificates for the links and test links domains through your DNS provider's API. Behind Cloudflare, keep the link records DNS-only (grey cloud) so Caddy terminates TLS, or use a Cloudflare Origin certificate.
Install
Once DNS points at the server, run:
curl -fsSL https://github.com/grovs-io/self-host/releases/latest/download/install.sh | bashThe installer comes from the latest completed release and selects its matching GHCR images. It:
- Downloads the stack into
./grovs. - Asks for your app domain, links domain, test links domain and admin email.
- Generates every secret and pulls the images.
- Starts the stack with the
standaloneproxy.
An exported GROVS_VERSION overrides its default version. Press Enter at the first prompt to start a local trial on lvh.me instead (see Try locally).
Manual install
Read install.sh first if you prefer. The manual equivalent:
git clone https://github.com/grovs-io/self-host.git grovs && cd grovs
GROVS_RELEASE_URL="$(curl -fsSL -o /dev/null -w '%{url_effective}' https://github.com/grovs-io/self-host/releases/latest)"
GROVS_VERSION="${GROVS_RELEASE_URL##*/}" ./scripts/setup.sh # writes .env and prints your admin password
docker compose --profile standalone pull
docker compose --profile standalone up -d # drop the profile on platforms with their own proxysetup.sh writes every hostname from your domains. It generates the database and ClickHouse passwords, the Rails secrets, the OAuth pair and the admin password. It keeps an existing .env. Save the admin password it prints.
On first start, migrate runs the PostgreSQL and ClickHouse migrations and the seed (the OAuth app and your bootstrap admin). Then web and the workers start. Certificates are issued on the first request to each host.
First login
Open https://<DASHBOARD_HOST> and log in with the admin email and the password setup.sh printed. The bootstrap admin needs no SMTP or SSO.
Public sign-up is disabled. On first login the dashboard asks you to create your first project; there is no default project. Invite more members from the dashboard. Each invite produces a copyable link unless you configure SMTP.
A fresh install shows "Analytics temporarily unavailable" until the first rollup completes, about a minute after boot. Reload the page.
After you create a project, copy its API key for the SDKs. There is one key per environment, test and production.
Verify
# Backend health
curl -sS https://<API_HOST>/up # -> 200Then in the dashboard: log in, create a project, create a link and open it. Monitoring covers ongoing health checks.
Change settings
All settings live in .env in the install directory. Edit it, then apply:
docker compose --profile standalone up -d # real domain
docker compose -f docker-compose.yml -f docker-compose.local.yml up -d # local trialCompose restarts only the containers whose environment changed. The database, uploads and analytics stay.
- Never change the generated secrets after the first start:
SECRET_KEY_BASE, theACTIVE_RECORD_ENCRYPTION_*keys, and the database and ClickHouse passwords. Existing data is bound to them. - If you change a domain, update DNS first.
Every variable is listed in Configuration. Before real traffic, work through the production checklist.