# Configuration

> Every environment variable for a self-hosted installation, plus optional features, email and branding.

Source: https://docs.grovs.io/docs/self-hosting/configure/configuration

All settings live in `.env` in the install directory. `./scripts/setup.sh` writes it from the domains you enter: every hostname, every secret and the OAuth pair. You edit it only to turn on optional features, rebrand or tune capacity. Apply changes with:

```bash
docker compose --profile standalone up -d
```

## Version and hostnames

| Variable               | What it does                                                                                                                                                                                                                                                                                                                              |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GROVS_VERSION`        | Shared tag for `ghcr.io/grovs-io/backend` and `ghcr.io/grovs-io/dashboard`. Run the [latest release lookup](https://docs.grovs.io/docs/self-hosting/configure/images#select-the-latest-release) before setup to select a numbered version. Existing Compose installations can use `latest` to follow new images on each pull and upgrade. |
| `COMPOSE_PROJECT_NAME` | Compose project name, `grovs` by default.                                                                                                                                                                                                                                                                                                 |
| `DASHBOARD_HOST`       | Dashboard UI host.                                                                                                                                                                                                                                                                                                                        |
| `API_HOST`             | Dashboard and REST API host. Also serves uploaded assets and the `/up` health check.                                                                                                                                                                                                                                                      |
| `SDK_HOST`             | **Mobile, web and server SDK host.** This exact value is the SDK `baseURL`.                                                                                                                                                                                                                                                               |
| `MCP_HOST`             | MCP (Model Context Protocol) OAuth and API host.                                                                                                                                                                                                                                                                                          |
| `GO_HOST`              | Reserved for the quick-link helper. Not enabled on self-hosted deployments.                                                                                                                                                                                                                                                               |
| `PREVIEW_HOST`         | Link preview pages.                                                                                                                                                                                                                                                                                                                       |
| `LINKS_PROD_HOST`      | Production links host under `DOMAIN_LIVE`, given a pre-issued certificate by the proxy.                                                                                                                                                                                                                                                   |
| `LINKS_TEST_HOST`      | Test links host under `DOMAIN_TEST`, given a pre-issued certificate by the proxy.                                                                                                                                                                                                                                                         |
| `ACME_EMAIL`           | Email Let's Encrypt uses for certificate notices. Standalone proxy only.                                                                                                                                                                                                                                                                  |

The Caddy proxy and the dashboard container read the `*_HOST` variables. The backend derives its hosts from the domain variables below. Keep both sets consistent; `setup.sh` does.

## Domains

| Variable                               | What it does                                                                                                                                                                                                                    |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SERVER_HOST_PROTOCOL` / `SERVER_HOST` | Protocol and **bare app domain** (`acme.com`). The backend derives `api.`, `sdk.`, `go.`, `mcp.` and `preview.` from it. **Never set it to the `api.` host**: the backend refuses to boot when it starts with a reserved label. |
| `REACT_HOST_PROTOCOL` / `REACT_HOST`   | Protocol and host for dashboard links, for example in emails. Your dashboard host.                                                                                                                                              |
| `DOMAIN_LIVE`                          | Links domain. Production project links are `<project>.<DOMAIN_LIVE>`. Defaults to the app domain. A branded short domain such as `acme.link` also works.                                                                        |
| `DOMAIN_TEST`                          | Test links domain. `test.<DOMAIN_LIVE>` by default. A separate domain also works.                                                                                                                                               |
| `PREVIEW_BASE_URL`                     | Full URL of the preview host. Required for the preview page and [copy to clipboard](https://docs.grovs.io/docs/links/copy-to-clipboard).                                                                                        |
| `MCP_CONSENT_URL`                      | OAuth consent URL for MCP: your dashboard's `/mcp/authorize`.                                                                                                                                                                   |
| `S3_ASSET_PREFIX`                      | Public URL prefix for uploads: `https://<API_HOST>`. Uploads are served through the backend.                                                                                                                                    |

## Self-hosted mode

| Variable            | What it does                                                                                                                                                                                    |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GROVS_SELF_HOSTED` | Set to `true` by the stack. Disables billing and public sign-up, removes usage quotas, turns member invites into copyable links when there is no SMTP, and serves uploads through the API host. |

The published backend image is the Community edition. Enterprise features (revenue tracking, the [audit log](https://docs.grovs.io/docs/enterprise/audit-log), [single sign-on](https://docs.grovs.io/docs/enterprise/sso) and SCIM) need `GROVS_EE=true` **and** an Enterprise build that includes the `ee/` directory. The flag alone does nothing on this stack. Contact Grovs for an Enterprise image.

## Database, cache and analytics store

| Variable                                                                                                                                                                                                                                                                                   | What it does                                                                                                                   |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB`                                                                                                                                                                                                                                      | Credentials and name for the bundled PostgreSQL. Compose builds the connection string from these.                              |
| `POSTGRES_MAX_CONNECTIONS`                                                                                                                                                                                                                                                                 | Server-side connection ceiling. Keep it at or above `WEB_CONCURRENCY × RAILS_DB_POOL` plus the worker pools.                   |
| `CLICKHOUSE_PASSWORD`                                                                                                                                                                                                                                                                      | Password of the `grovs` ClickHouse user, generated by `setup.sh`.                                                              |
| `CLICKHOUSE_WRITE_ENABLED`, `CLICKHOUSE_READ_ENABLED`, `CLICKHOUSE_PRIMARY`, `CLICKHOUSE_ANALYTICS_ROLLUPS_READ_ENABLED`, `CLICKHOUSE_ATTRIBUTION_READ_ENABLED`, `CLICKHOUSE_LINK_DIMENSIONS_READ_ENABLED`, `CLICKHOUSE_ROLLUP_FAST_LANE`, `REVENUE_READS_FROM_LEDGER`, `PG_SHADOW_WRITES` | ClickHouse is the event store and serves every analytics read. PostgreSQL keeps a spill fallback only. Leave these as shipped. |
| `DASHBOARD_CACHE_TTL_SECONDS`                                                                                                                                                                                                                                                              | Cache lifetime for dashboard analytics reads. 60 by default.                                                                   |

`DATABASE_URL`, `REDIS_URL` and `CLICKHOUSE_URL` are not in `.env`; Compose sets them for the containers. For a custom deployment on managed services, set them on the backend containers directly.

## Process sizing

Raise these as you add CPU and RAM. [Sizing](https://docs.grovs.io/docs/self-hosting/operate/sizing#process-sizing) lists the shipped values, the connection budget and what to raise first.

| Variable                     | What it does                                |
| ---------------------------- | ------------------------------------------- |
| `WEB_CONCURRENCY`            | Puma worker processes in the web container. |
| `RAILS_MAX_THREADS`          | Threads per Puma worker.                    |
| `RAILS_DB_POOL`              | Database connection pool per process.       |
| `SIDEKIQ_EVENTS_CONCURRENCY` | Threads for the events worker.              |

## Uploads

| Variable                                                                  | What it does                                                                                                             |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `ACTIVE_STORAGE_SERVICE`                                                  | `local` (default) keeps uploads in the `storage` volume. `amazon` uses S3 with the variables below.                      |
| `AWS_S3_KEY_ID` / `AWS_S3_ACCESS_KEY` / `AWS_S3_REGION` / `AWS_S3_BUCKET` | S3 credentials, region and bucket.                                                                                       |
| `S3_ENDPOINT` / `S3_FORCE_PATH_STYLE`                                     | Only for S3-compatible stores you run yourself (MinIO and similar): the endpoint URL and `true`. Leave empty for AWS S3. |

## Secrets and keys

`setup.sh` generates these. Never change them after the first start.

| Variable                                                                             | What it does                                                                                                                                                               |
| ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SECRET_KEY_BASE`                                                                    | Rails session and signing secret.                                                                                                                                          |
| `ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY`, `_DETERMINISTIC_KEY`, `_KEY_DERIVATION_SALT` | At-rest encryption keys. Rotating them makes encrypted columns unreadable.                                                                                                 |
| `ADMIN_API_KEY` / `DIAGNOSTICS_API_KEY` / `SENT_QUOTAS_WEBHOOK_KEY`                  | Keys guarding internal admin, diagnostics and webhook endpoints.                                                                                                           |
| `OAUTH_CLIENT_UID` / `OAUTH_CLIENT_SECRET`                                           | The dashboard's OAuth application. The seed upserts the application to this pair and the dashboard container reads it at start. Nothing is baked into the dashboard image. |
| `PUBLIC_GO_PROJECT_IDENTIFIER`                                                       | Quick-link project used by the managed cloud. Not seeded on self-hosted deployments, so the value is unused.                                                               |

The image and Compose file fix `RAILS_ENV`, `RAILS_SERVE_STATIC_FILES` and logging to stdout. To change log verbosity, set `RAILS_LOG_LEVEL` on the backend containers. The default is `error`.

## First-run admin

| Variable                                             | What it does                                                                                                               |
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `BOOTSTRAP_ADMIN_EMAIL` / `BOOTSTRAP_ADMIN_PASSWORD` | The seed creates this admin so you can log in without SMTP or SSO. After login you are asked to create your first project. |

## Local trial

`setup.sh` writes these only for the `lvh.me` trial, with `http://` protocols:

| Variable               | Default |
| ---------------------- | ------- |
| `GROVS_LOCAL`          | `true`  |
| `GROVS_WEB_PORT`       | `80`    |
| `GROVS_DASHBOARD_PORT` | `3002`  |

Set the two ports before running the installer to change them.

## Optional features

Each is off by default. Add the lines to `.env`, then run `docker compose --profile standalone up -d`.

### Custom domains

Let a project serve links on a domain your customer owns (`links.customer.com`) instead of `<project>.<links domain>`:

```bash
CUSTOM_DOMAINS_ENABLED=true
CUSTOM_DOMAINS_PROVIDER=manual
SELF_HOSTED_INGRESS_HOST=links.acme.link     # the host customers point their CNAME at; defaults to SERVER_HOST
```

For each domain:

1. The customer creates `CNAME links.customer.com → <SELF_HOSTED_INGRESS_HOST>`.
2. An admin adds the domain on the project's Domain page.
3. Grovs verifies it within a minute by probing `https://links.customer.com/.well-known/grovs-domain-verification`.

The bundled proxy issues the certificate on that first request. Behind your own proxy, attach a certificate for the host before the customer switches DNS. See [Custom domains](https://docs.grovs.io/docs/links/custom-domains).

### Link migration from Branch or AppsFlyer

```bash
MIGRATIONS_ENABLED=true
```

Then set up the migration on the project's Domain page with the provider credentials. If the old links live on a domain you will point at Grovs, enable custom domains too. See [Migration guides](https://docs.grovs.io/docs/migration-guides).

### Email

Email is off by default. The bootstrap admin needs none, and member invites produce a copyable link. To send password-reset, invite and data-export emails, set:

| Variable                                     | What it does                                                                                                                               |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `MAILER_DELIVERY_METHOD`                     | `smtp` to send mail. Leave empty to run without email: invites become copyable links, and password resets and exports cannot be delivered. |
| `SMTP_ADDRESS` / `SMTP_PORT` / `SMTP_DOMAIN` | SMTP server address, port and HELO domain.                                                                                                 |
| `SMTP_USERNAME` / `SMTP_PASSWORD`            | SMTP authentication.                                                                                                                       |
| `SMTP_AUTHENTICATION`                        | `plain`, `login` or `cram_md5`.                                                                                                            |
| `SMTP_ENABLE_STARTTLS_AUTO`                  | `true` to use STARTTLS when available.                                                                                                     |
| `MAILER_FROM`                                | From address for outgoing email.                                                                                                           |

## Branding and link images

Link landing pages and social previews use the per-project and per-link images you set in the dashboard. When those are missing, they fall back to the defaults below.

### Default images

The defaults point at the Grovs assets, so links are never blank out of the box. Set your own URLs to rebrand.

| Variable                     | Used for                                                                |
| ---------------------------- | ----------------------------------------------------------------------- |
| `DEFAULT_LOGO_URL`           | App icon on the link landing page when a project has no app-store icon. |
| `DEFAULT_SOCIAL_PREVIEW_URL` | OG and Twitter card image when a link has no custom preview image.      |
| `DEFAULT_LINK_TITLE`         | Default `og:title` for link previews.                                   |
| `DEFAULT_LINK_SUBTITLE`      | Default `og:description` for link previews.                             |

> **Warning:** Do not blank these. With no value, link pages show an empty icon and social shares render an empty card. Use publicly reachable image URLs. A **1200×630** JPG or PNG works well for the social preview. A per-link image set in the dashboard overrides the defaults.

### Uploaded images must be reachable

App icons and preview images you upload are stored in the `storage` volume (or S3) and served through the **API host**. For them to appear, `S3_ASSET_PREFIX` must be `https://<API_HOST>` and that host must be publicly reachable over HTTPS. A wrong `S3_ASSET_PREFIX` is the usual cause of an uploaded image not showing.

Facebook, LinkedIn, iMessage and others cache OG data aggressively. If a shared link still looks blank after you fix the image, re-scrape it (for example with Facebook's Sharing Debugger) or test with a fresh link.
