Self-hosting
Run Fettlecode on your own server
Fettlecode is one server, a PostgreSQL database and a folder of plain Git repositories. This page says what it needs, how sign-in and Git are set up, and every setting it reads.
Not released yet. Fettlecode is in development. Its source code isn't public, and the Docker image and Compose file are part of the work for 1.0, so there's nothing to install today. The settings below are the ones the server reads now; they may still change before the first release.
What it needs
- The Fettlecode server, which serves the app, its API and Git
- PostgreSQL 18, for accounts, reviews, comments and settings
-
Git 2.40 or later on the server: Fettlecode drives the real
git, and never needs a working copy - A folder for data: bare repositories, Git LFS files and the SSH host key
- Optionally, an SMTP server for invitations and notifications
Installing it
The plan for 1.0 is a published Docker image and a Compose file, with backups and full docs. These are the steps we're planning; they'll be confirmed when it's released:
- Install Docker with Compose v2: Docker Desktop on Windows and macOS, or Docker Engine on Linux.
- In the Fettlecode folder, start it:
docker compose up -d
Then open it and create your account. The first person to sign up becomes the instance
admin, and sign-up closes behind them: everyone else needs an invitation or single
sign-on, unless you set ALLOW_SIGNUP=true.
Running it for a team
Fettlecode reads its settings from the environment. A minimal set:
# The address people use. No trailing slash.
APP_URL=https://code.example.com
# PostgreSQL, and where repositories are kept
DATABASE_URL=postgres://fettlecode:<password>@db:5432/fettlecode
DATA_DIR=/var/lib/fettlecode
# Signs sessions. At least 32 characters.
AUTH_SECRET=<openssl rand -hex 32>
# Behind a reverse proxy, so client addresses are right
TRUST_PROXY=true
# Email: invitations and notifications
SMTP_URL=smtps://user:[email protected]:465
MAIL_FROM=Fettlecode <[email protected]>
Fettlecode checks every setting when it starts. If one is wrong or missing, it stops and names it, rather than half-working, and it never prints a secret's value.
With no mail server, nothing breaks: organisation owners copy an invitation link and send it themselves.
Git over HTTPS and SSH
Clone, fetch and push over HTTPS with a personal access token as the password, or over
SSH with a key added in Settings. SSH listens on port 2222 by default; set
SSH_PORT to change it, or to 0 to turn it off.
# Clone over HTTPS or SSH
git clone https://code.example.com/acme/api.git
git clone ssh://[email protected]:2222/acme/api.git
# The hook that adds a Change-Id to each commit
curl -o .git/hooks/commit-msg https://code.example.com/tools/commit-msg
chmod +x .git/hooks/commit-msg
Git LFS uses the same address and token. LFS_MAX_OBJECT_MB caps a single
file, at 2,048 MB by default.
Single sign-on
All of these are in every copy. Use as many as you need.
OpenID Connect
Register Fettlecode with your provider using the redirect address
<APP_URL>/api/auth/callback/<id>, then set:
OIDC_ID=keycloak
OIDC_NAME=Keycloak
OIDC_ISSUER=https://sso.example.com/realms/acme
OIDC_CLIENT_ID=fettlecode
OIDC_CLIENT_SECRET=...
For several providers, set OIDC_PROVIDERS to a JSON list of
{id, name, issuer, clientId, clientSecret}. People who arrive through OIDC
for the first time get an account, unless you set OIDC_ALLOW_SIGNUP=false.
SAML
Give your identity provider (Okta, Entra ID, Google Workspace, ADFS and others) this
address:
<APP_URL>/api/auth/sso/saml2/sp/metadata?providerId=saml. Then point
Fettlecode at the identity provider's metadata file:
SAML_IDP_METADATA=/etc/fettlecode/idp-metadata.xml
SAML_NAME=Acme sign-in
# Existing accounts with these email domains may sign in through SAML
SAML_DOMAIN=example.com
Instead of a metadata file, you can set SAML_ENTRY_POINT,
SAML_IDP_ENTITY_ID and SAML_IDP_CERT together.
LDAP
Directory sign-in works with Active Directory, OpenLDAP and others. Setting
LDAP_URL and LDAP_BASE_DN turns it on:
LDAP_URL=ldaps://directory.example.com
LDAP_BASE_DN=dc=example,dc=com
LDAP_BIND_DN=cn=fettlecode,ou=services,dc=example,dc=com
LDAP_BIND_PASSWORD=...
SCIM provisioning
Each organisation's owners make a SCIM token in Settings, under Provisioning, and give
your identity provider the address <APP_URL>/scim/v2. It then adds,
updates and deactivates members, and keeps teams in step with its groups.
Connecting GitHub
Importing from GitHub needs no settings. Two other parts do, and both work with GitHub
Enterprise Server too, through GITHUB_URL and GITHUB_API_URL.
-
Sign in with GitHub: make a GitHub OAuth app whose callback is
<APP_URL>/api/auth/callback/github, and setGITHUB_CLIENT_IDandGITHUB_CLIENT_SECRET. Only people who are invited, in an organisation listed inGITHUB_ALLOWED_ORGS, or anyone whenGITHUB_ALLOW_SIGNUP=true, can create an account this way. -
The pull request bridge: each repository can use a token of its own, or
the instance's GitHub App (
GITHUB_APP_ID,GITHUB_APP_PRIVATE_KEYandGITHUB_APP_WEBHOOK_SECRET). The app's webhook address is<APP_URL>/webhooks/github, for pull request events; it needs read and write on pull requests and commit statuses, and read on contents.
Where your data lives
-
Repositories are plain bare Git repositories, under
DATA_DIR/repos/<owner>/<name>.git - Git LFS files are under
DATA_DIR/lfs - The SSH host key is under
DATA_DIR/ssh - Everything else, from accounts to reviews and comments, is in PostgreSQL. Secrets for connected services are encrypted there.
Backup and restore scripts, and a JSON export of reviews and comments, are planned. Until they arrive, back up the data folder and the database together.
All settings
| Setting | Default | What it does |
|---|---|---|
APP_URL |
Required | The address people use to reach Fettlecode, with no trailing slash |
DATABASE_URL |
Required | The PostgreSQL connection string |
AUTH_SECRET |
Required | Signs sessions. 32 characters or more |
DATA_DIR |
./data |
Where repositories, Git LFS files and the SSH host key are kept |
PORT, HOST |
3200, 0.0.0.0 | Where the server listens |
TRUST_PROXY |
false | Trust the reverse proxy's forwarded client address |
ALLOW_SIGNUP |
false | Let anyone create an account |
LOG_LEVEL |
info | From fatal to trace |
MAX_CHANGES_PER_PUSH |
50 | The most commits one push for review may turn into changes (up to 500) |
LFS_MAX_OBJECT_MB |
2048 | The largest single file Git LFS may store |
SSH_PORT, SSH_HOST |
2222, APP_URL's host |
Git over SSH, and the host shown in SSH clone addresses. Port 0 turns it off |
SMTP_URL, MAIL_FROM |
Off | Outgoing email, as smtp:// or smtps:// |
INTEGRATIONS_ALLOW_PRIVATE |
true | Let repositories connect Fettleboard or Fettledesk on a private address. Set it to false on a shared host |
OIDC_ID, OIDC_NAME, OIDC_ISSUER,
OIDC_CLIENT_ID, OIDC_CLIENT_SECRET
|
Off | One OpenID Connect provider |
OIDC_PROVIDERS |
Off | Several OpenID Connect providers, as a JSON list |
OIDC_ALLOW_SIGNUP |
true | People arriving through OIDC for the first time get an account |
LDAP_URL, LDAP_BASE_DN |
Off | Turn on directory sign-in |
LDAP_BIND_DN, LDAP_BIND_PASSWORD |
None | The account Fettlecode searches the directory with |
LDAP_USER_FILTER |
uid, sAMAccountName or mail | How a username is found, with {username} in the filter |
LDAP_NAME, LDAP_NAME_ATTR, LDAP_MAIL_ATTR |
Directory, displayName, mail | The name on the sign-in page, and which attributes hold the name and email |
LDAP_ALLOW_SIGNUP, LDAP_VERIFY_TLS |
true, true | Create accounts on first sign-in; check the directory's certificate |
SAML_IDP_METADATA |
Off | The path to the identity provider's metadata file |
SAML_ENTRY_POINT, SAML_IDP_ENTITY_ID,
SAML_IDP_CERT
|
Off | The identity provider, set by hand instead of a metadata file |
SAML_NAME, SAML_DOMAIN |
Single sign-on, none | The name on the sign-in page; email domains whose existing accounts SAML may sign in to |
SAML_EMAIL_ATTR, SAML_NAME_ATTR |
email, displayName | Which attributes hold the email and name |
SAML_ALLOW_SIGNUP, SAML_WANT_ASSERTIONS_SIGNED |
true, false | Create accounts on first sign-in; require signed assertions |
GITHUB_URL, GITHUB_API_URL |
github.com, api.github.com | Where GitHub is; change both for GitHub Enterprise Server |
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET |
Off | Sign in with GitHub, through a GitHub OAuth app |
GITHUB_ALLOWED_ORGS, GITHUB_ALLOW_SIGNUP |
None, false | Who may create an account by signing in with GitHub |
GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY,
GITHUB_APP_WEBHOOK_SECRET
|
Off | A GitHub App for the pull request bridge. The key is a PEM, or a path to one |