Docs
grovs.io Start for free

Configuration

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

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

VariableWhat it does
GROVS_VERSIONShared tag for ghcr.io/grovs-io/backend and ghcr.io/grovs-io/dashboard. Run the latest release lookup before setup to select a numbered version. Existing Compose installations can use latest to follow new images on each pull and upgrade.
COMPOSE_PROJECT_NAMECompose project name, grovs by default.
DASHBOARD_HOSTDashboard UI host.
API_HOSTDashboard and REST API host. Also serves uploaded assets and the /up health check.
SDK_HOSTMobile, web and server SDK host. This exact value is the SDK baseURL.
MCP_HOSTMCP (Model Context Protocol) OAuth and API host.
GO_HOSTReserved for the quick-link helper. Not enabled on self-hosted deployments.
PREVIEW_HOSTLink preview pages.
LINKS_PROD_HOSTProduction links host under DOMAIN_LIVE, given a pre-issued certificate by the proxy.
LINKS_TEST_HOSTTest links host under DOMAIN_TEST, given a pre-issued certificate by the proxy.
ACME_EMAILEmail 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

VariableWhat it does
SERVER_HOST_PROTOCOL / SERVER_HOSTProtocol 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_HOSTProtocol and host for dashboard links, for example in emails. Your dashboard host.
DOMAIN_LIVELinks domain. Production project links are <project>.<DOMAIN_LIVE>. Defaults to the app domain. A branded short domain such as acme.link also works.
DOMAIN_TESTTest links domain. test.<DOMAIN_LIVE> by default. A separate domain also works.
PREVIEW_BASE_URLFull URL of the preview host. Required for the preview page and copy to clipboard.
MCP_CONSENT_URLOAuth consent URL for MCP: your dashboard's /mcp/authorize.
S3_ASSET_PREFIXPublic URL prefix for uploads: https://<API_HOST>. Uploads are served through the backend.

Self-hosted mode

VariableWhat it does
GROVS_SELF_HOSTEDSet 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, single sign-on 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

VariableWhat it does
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DBCredentials and name for the bundled PostgreSQL. Compose builds the connection string from these.
POSTGRES_MAX_CONNECTIONSServer-side connection ceiling. Keep it at or above WEB_CONCURRENCY × RAILS_DB_POOL plus the worker pools.
CLICKHOUSE_PASSWORDPassword 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_WRITESClickHouse is the event store and serves every analytics read. PostgreSQL keeps a spill fallback only. Leave these as shipped.
DASHBOARD_CACHE_TTL_SECONDSCache 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 lists the shipped values, the connection budget and what to raise first.

VariableWhat it does
WEB_CONCURRENCYPuma worker processes in the web container.
RAILS_MAX_THREADSThreads per Puma worker.
RAILS_DB_POOLDatabase connection pool per process.
SIDEKIQ_EVENTS_CONCURRENCYThreads for the events worker.

Uploads

VariableWhat it does
ACTIVE_STORAGE_SERVICElocal (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_BUCKETS3 credentials, region and bucket.
S3_ENDPOINT / S3_FORCE_PATH_STYLEOnly 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.

VariableWhat it does
SECRET_KEY_BASERails session and signing secret.
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY, _DETERMINISTIC_KEY, _KEY_DERIVATION_SALTAt-rest encryption keys. Rotating them makes encrypted columns unreadable.
ADMIN_API_KEY / DIAGNOSTICS_API_KEY / SENT_QUOTAS_WEBHOOK_KEYKeys guarding internal admin, diagnostics and webhook endpoints.
OAUTH_CLIENT_UID / OAUTH_CLIENT_SECRETThe 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_IDENTIFIERQuick-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

VariableWhat it does
BOOTSTRAP_ADMIN_EMAIL / BOOTSTRAP_ADMIN_PASSWORDThe 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:

VariableDefault
GROVS_LOCALtrue
GROVS_WEB_PORT80
GROVS_DASHBOARD_PORT3002

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.

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.

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:

VariableWhat it does
MAILER_DELIVERY_METHODsmtp 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_DOMAINSMTP server address, port and HELO domain.
SMTP_USERNAME / SMTP_PASSWORDSMTP authentication.
SMTP_AUTHENTICATIONplain, login or cram_md5.
SMTP_ENABLE_STARTTLS_AUTOtrue to use STARTTLS when available.
MAILER_FROMFrom address for outgoing email.

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.

VariableUsed for
DEFAULT_LOGO_URLApp icon on the link landing page when a project has no app-store icon.
DEFAULT_SOCIAL_PREVIEW_URLOG and Twitter card image when a link has no custom preview image.
DEFAULT_LINK_TITLEDefault og:title for link previews.
DEFAULT_LINK_SUBTITLEDefault og:description for link previews.

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.

Last updated 2026-10-26
Was this page helpful?