Fruitback Docs
fruitback.com GitHub
All the docs

Docs Reference Environment variables

Environment variables

Grouped as in .env.example. Refused means the worker starts, logs misconfigured, missing: …, answers /health with 503 naming the variable, and every other route with 500. Docker then marks the container unhealthy, and docker compose up --wait fails. An empty value counts as absent.

The image and the port

Variable Default What it does When it is wrong
FRUITBACK_IMAGE ghcr.io/sakuga-software/fruitback-worker:edge Compose only. The image, as a tag or a digest. A tag that does not exist fails the pull. A tag already on the machine is not pulled again: run docker compose pull.
FRUITBACK_PORT 8080 Compose only. The port on the host, or 127.0.0.1:8080 to keep it off the internet. An older .env says PORT, which the file ignores: the port falls back to 8080.
FRUITBACK_CONSOLE_IMAGE ghcr.io/sakuga-software/fruitback-console:edge Compose only. The image of the console, as a tag or a digest. See The console. A tag that does not exist fails the pull of the console, and of no other service.
FRUITBACK_CONSOLE_PORT 8081 Compose only. The port of the console on the host, or 127.0.0.1:8081 to keep it off the internet. The same port as FRUITBACK_PORT: the second container does not start.
PORT 8080 The port the process listens on, inside the container. Compose sets it to 8080 and does not read it from .env. A value other than the published port makes the worker unreachable, and Docker still reports it healthy, because the healthcheck probes the same port (measured with 9000). With docker run, a value that is not a positive integer: the process listens on 8080 with no message, but the healthcheck probes the raw value, so Docker marks the container unhealthy (measured with abc).
HOST 0.0.0.0 The interface the process listens on. Compose does not pass it. 127.0.0.1 makes the worker unreachable from outside the container, and Docker still reports it healthy (measured).
NODE_ENV production, set by the image What refuses the in-memory store. Compose does not pass it. Any other value lets FRUITBACK_STORE=memory start, and every note dies with the container.

Who may call the worker

Variable Default What it does When it is wrong
ALLOWED_ORIGINS none: required Origins that may call the worker, comma-separated, or *. The address of each site of the console joins the list by itself (measured). Absent: Compose does not start. With docker run, the container runs and is refused: /health answers 503 naming it, and Docker marks it unhealthy (measured). An origin that is not exactly what the browser sends (a trailing slash, http for https, a missing port) is accepted at boot, and each call from the site answers 403 origin-not-allowed, which the browser shows as a CORS error. A request with no Origin, such as curl, is not checked. * accepts every site. An origin of the list that is no site of the console plants nothing: a write names a site, and the page of another origin gets 403 origin-not-allowed-for-client (measured).
TRUSTED_PROXY_HOPS 1 in the worker, 0 in docker-compose.yml The number of proxies between the internet and the container. Not a whole number from 0 up: refused. Too high or too low: see Behind a reverse proxy.
RATE_LIMIT_PER_MINUTE 20 Requests a minute per client address, reads and writes together. Counted in the database, so the replicas share one limit. /health is not counted. Not a whole number above 0: refused. Too low for a team behind one office address: they share a bucket and get 429 rate-limited.

Where the notes live

Variable Default What it does When it is wrong
FRUITBACK_STORE postgres Where the notes of a site with no tracker of its own go: postgres (the database of FRUITBACK_DATABASE_URL), linear, github or memory. Unknown: refused, FRUITBACK_STORE (unknown store "postgre", expected …), with the list. The store of a file, of before 0.3.0: refused, with what to do: see Upgrading. Unset while LINEAR_API_KEY, LINEAR_TEAM_ID or a FRUITBACK_GITHUB_ variable is set: refused, because a worker of before with no store ran on Linear; write the store. memory in the image: refused.
FRUITBACK_SQLITE_PATH none Gone since 0.3.0. It named the file of the notes of a worker of before. The notes are in the database; migrate-to-postgres --notes copies a file there. Set: refused, with what to do. Compose hands it over empty for that reason only.
LINEAR_API_KEY none A Linear personal API key. A secret: it never reaches a browser. Absent with linear: refused, with LINEAR_TEAM_ID. Wrong: /health answers 200, and every read and write answers 502 store-unavailable, Linear responded 401 (measured).
LINEAR_TEAM_ID none The team that receives the issues. A client’s teamId replaces it. Absent with linear: refused. Wrong: not checked at boot; Linear refuses the call, and the worker answers 502 store-unavailable with Linear’s message.
LINEAR_PROJECT_ID none The project for the issues. Optional. A client’s projectId replaces it. Wrong: not checked at boot.
FRUITBACK_GITHUB_APP_ID none The ID of the GitHub App, or its client ID. Absent with github: refused, with the two other FRUITBACK_GITHUB_ variables. Wrong: not checked at boot; GitHub refuses the App’s token, and every read and write answers 502 store-unavailable.
FRUITBACK_GITHUB_PRIVATE_KEY none The App’s private key, the .pem file GitHub generates. A secret. On one line, write each line break as \n; inside double quotes, the key can keep its line breaks. Both forms reach the worker as a key it reads (measured with docker compose config). Absent with github: refused. Not an RSA private key: refused, and the diagnostic names the variable, never the key. The key of another App: every read and write answers 502 store-unavailable.
FRUITBACK_GITHUB_REPOSITORY none The repository that receives the issues, as owner/repo. A client’s repository replaces it. Absent with github: refused. Not owner/repo: refused. A repository the App is not installed on: /health answers 200, and every read and write answers 502 store-unavailable.

Several client sites

Variable Default What it does When it is wrong
FRUITBACK_CLIENTS none Gone since 0.3.0. It was the map of the sites of a worker with no database. The console makes each site, in the database. Set, whatever it holds: refused, with what to do: make each site in the console, then copy its notes with migrate-to-postgres --notes <file> --client <id>=<id of the site>.

Identity and reads

Variable Default What it does When it is wrong
FRUITBACK_IDENTITY_SECRET none: required The HS256 key that signs the access token of a session of the extension, 32 characters or more. Give it to no site: see Change FRUITBACK_IDENTITY_SECRET. Make one with openssl rand -base64 32. Absent: refused. Under 32 characters, or with white space at its start or its end: refused. Changed: every reviewer’s access token answers 401 until its next refresh.
FRUITBACK_READ none It applies to no site: each site of the console has its visibility, members or everyone, and the boot log says so (measured). It was the setting of a worker of before 0.3.0, public or authenticated. A typo: refused, never defaulted to public.
FRUITBACK_HIDE_COMMENTS empty: the team’s replies are shown 1 keeps the team’s replies out of the pins. Any other value, true included, is accepted and hides nothing (measured).
FRUITBACK_NO_UPDATE_CHECK empty: the worker asks GitHub for the last release 1: the worker never asks. Empty: when an administrator opens « Instance » in the console, the worker asks GitHub for the last release, once a day at most, and the screen says when a newer one is out. The request carries nothing of the instance, but GitHub sees the address of the server. Any other value, true included, is accepted and stops nothing.
FRUITBACK_TEAM_LOCALE en The language of the prose in an issue description, for the team that triages it: en or fr. It never follows the reporter’s browser, and the note itself is never translated. A value that is not a locale tag: refused at boot, named in missing. A valid tag this build has no words for, de for example: the description is in English (measured on the round trip, which holds in every language because only the prose is translated).

The database, the console and the sessions

Variable Default What it does When it is wrong
FRUITBACK_SESSION_PATH none Gone since 0.3.0. It named the file of the sessions of the extension, on a worker of before. The sessions are in the database. Set: refused, with what to do. Each reviewer pairs again, once, from the console.
FRUITBACK_FIRST_REVIEWER none Gone since 0.3.0. It paired the first reviewer of a worker with no workspace. The first administrator is FRUITBACK_FIRST_ADMIN, and a reviewer pairs from the console. Set: refused, with what to do.
FRUITBACK_FIRST_ADMIN none: no link is written when the worker starts The address of the first administrator, for an instance that sends no e-mail and has no provider. A worker whose instance has no owner yet writes a sign-in link for that address in its log when it starts. See The first administrator. Not one address: refused. Without FRUITBACK_AUTH_SECRET and its database: refused, and the worker does not make the secret.
FRUITBACK_ACCOUNTS_PATH none Gone since 0.3.0. It named the file of the accounts. The accounts are in PostgreSQL, with FRUITBACK_DATABASE_URL. Set: refused at boot, and the log says what to set. See Upgrading a deployment that has an accounts file.
FRUITBACK_DATABASE_URL none: required; in docker-compose.yml, the postgres service of the file The address of the PostgreSQL database that holds everything the worker keeps: the notes, the accounts, the workspaces and their sites, the connectors and the sessions, as postgres://user:password@host:5432/database. The worker makes its tables at the first request, so the role of the address has CREATE on the schema of the worker: public, or the one of FRUITBACK_DATABASE_SCHEMA. The worker sets the search path of each of its connections itself: the one of the address, of the role or of the database has no effect. Absent: refused, with the four variables below and a word on Upgrading. Not a postgres:// address with a host: refused. With FRUITBACK_SESSION_PATH or FRUITBACK_CLIENTS: refused. It holds a password: no log of the worker and no answer of /health quotes it. A database that does not answer, or a role that cannot create in the schema of the worker: the worker answers 502 to every read and every write, the log says why, and /health stays green.
FRUITBACK_DATABASE_SCHEMA public The schema of that database for the tables of the worker and its journal of migrations: see In a database that another application uses. Not a plain name (a lower-case letter or _, then lower-case letters, digits and _, 63 characters at most, not pg_… or information_schema, and not a key word of SQL such as user, order or table): refused, and the value is not quoted. Set without FRUITBACK_DATABASE_URL: refused. A schema that is not there and a role that cannot create in the database: 502, and the log says what to grant.
FRUITBACK_POSTGRES_PASSWORD none: required by docker-compose.yml The password of the PostgreSQL that docker-compose.yml starts beside the worker. The worker does not read it: the compose file puts it in the address it gives the worker. Make one with openssl rand -hex 32: it goes into an address. Absent: docker compose starts nothing, and names it. Changed after the first start: the database keeps the password it was made with, and the worker cannot connect.
FRUITBACK_CONSOLE_URL none: required; http://localhost:8081 in .env.example Where the console answers. A sign-in link opens it, and the console routes answer this origin only, with credentials. Absent: refused. Plain http:// on a host other than localhost: refused.
FRUITBACK_MAIL_FROM none: no sign-in by link The address the sign-in links come from, on a domain the provider verified. Set without FRUITBACK_SCALEWAY_TEM: refused. A domain the provider did not verify: /console/auth/email answers 502 mail-unavailable.
FRUITBACK_SCALEWAY_TEM none: no sign-in by link Scaleway Transactional Email, as <project id>:<secret key> of an API key that may send. Set without FRUITBACK_MAIL_FROM, or with no colon: refused. A revoked key: /console/auth/email answers 502 mail-unavailable.
FRUITBACK_PUBLIC_URL none: required; http://localhost:8080 in .env.example The address this worker answers at, as a browser reaches it, behind its proxy. The console calls it, and GitHub, Google and Linear send a person back to it. Absent: refused. Plain http:// on a host other than localhost, or with a path: refused.
FRUITBACK_GITHUB_OAUTH none Gone since 0.3.0. It named the GitHub OAuth App of the sign-in of before. FRUITBACK_AUTH_GITHUB_OAUTH is the one that signs in. Set: refused at boot, and the log says what to remove.
FRUITBACK_GOOGLE_OAUTH none: no sign-in with Google A Google OAuth client of type « Web application », as <client id>:<client secret>. Its redirect URI is <FRUITBACK_PUBLIC_URL>/console/auth/callback/google. No colon, or set without FRUITBACK_AUTH_SECRET: refused. A redirect URI that differs from the client’s: Google refuses the sign-in.
FRUITBACK_LINEAR_OAUTH none: a workspace connects Linear with a key A Linear OAuth application, as <client id>:<client secret>. Its callback is <FRUITBACK_PUBLIC_URL>/auth/linear/callback. No colon, or set without FRUITBACK_PUBLIC_URL, FRUITBACK_DATABASE_URL or FRUITBACK_SECRETS_KEY: refused. A callback that differs from the application’s: Linear refuses the consent.
FRUITBACK_GITHUB_CONNECTOR none: a workspace connects no GitHub The GitHub App that a workspace installs to connect its GitHub, as <app id>:<client id>:<client secret>. Another App than the one of the store github. Set without FRUITBACK_GITHUB_CONNECTOR_PRIVATE_KEY, not three parts, an App ID that is not digits, or set without FRUITBACK_DATABASE_URL or FRUITBACK_SECRETS_KEY: refused.
FRUITBACK_GITHUB_CONNECTOR_PRIVATE_KEY none The private key of that App, the .pem file GitHub generates. A secret: it mints a token for every repository of every organisation that installed the App. On one line, write each line break as \n; with its line breaks, the worker reads it as it is. Set without FRUITBACK_GITHUB_CONNECTOR, or not an RSA private key: refused, and the diagnostic names the variable, never the key.
FRUITBACK_SECRETS_KEY none: a workspace connects no tracker What encrypts, in the database, the key of each connector a workspace adds. At least 32 characters. With white space at its start or its end: refused. Equal to FRUITBACK_IDENTITY_SECRET, white space aside, or set without FRUITBACK_DATABASE_URL: refused. Changed or lost: every connector answers 502, and each workspace adds its key again.
FRUITBACK_AUTH_SECRET none: required What better-auth signs the session cookie of the console with. The worker builds better-auth when it starts, says so in its log, and serves its sign-in under /console/auth. At least 32 random characters. Make one with openssl rand -base64 32. Absent: refused: an administrator can always sign in, and the worker makes no secret. Under 32 characters, or with white space at its start or its end (a value of spaces, a line that kept one): refused. Equal to FRUITBACK_IDENTITY_SECRET or FRUITBACK_SECRETS_KEY, white space aside: refused. With a BETTER_AUTH_… variable in the environment: refused, because better-auth reads those over what the worker gives it. Changed or lost: every session of the console ends, and everybody signs in again.
FRUITBACK_AUTH_GITHUB_OAUTH none: no sign-in with GitHub A GitHub OAuth App, as <client id>:<client secret>. Its callback is <FRUITBACK_PUBLIC_URL>/console/auth/callback/github. No colon: refused. Without FRUITBACK_AUTH_SECRET: refused.
FRUITBACK_AUTH_TRACK_IP none: no address and no user agent kept 1: better-auth keeps the address and the user agent of each session of the console, and limits by address too, with the address the worker read behind its trusted proxies. Any value but 1: refused. Without FRUITBACK_AUTH_SECRET: refused.
FRUITBACK_SIGN_UP invited: the people a workspace invites, and nobody else Who may create an account. invited: a sign-in that would create an account is refused, except for an address that a workspace invited and whose invitation waits, the first account of the instance, and the link of FRUITBACK_FIRST_ADMIN or of admin-link. The console tells a person who is refused to ask for an invitation. anyone: every person who signs in gets an account, and can make a workspace. A person who has an account signs in with either value. Another value, Anyone included: refused, with the two values.
FRUITBACK_FAKE_LINEAR none The old spelling of FRUITBACK_STORE=memory. Deprecated. In the image it is ignored, and the boot log says so. Delete the line.
FRUITBACK_DEV_DATABASE none Development only: pglite gives the worker of pnpm dev a PostgreSQL in its process. Compose does not pass it. In the image: refused, /health answers 503 and names it.
FRUITBACK_DEV_VISIBILITY none Development only: read by the worker of pnpm dev, for the site it makes. Compose does not pass it. The image does not read it.
FRUITBACK_DEV_LINEAR none Development only: fake makes the worker of the bench of the extension send the notes of its site to a fake Linear. Compose does not pass it. The image does not read it.