Self-host

Run your own.

One release and a Postgres. Pick how you'd like to run it, tell it where it will live, and paste the steps in order.

  1. 1 Start a database

    Postgres on a private network the forge joins; nothing is published to the host.

    podman network create gitgud
    podman run -d --restart unless-stopped --network gitgud --name gitgud-db \
      -e POSTGRES_USER=gitgud -e POSTGRES_PASSWORD=change-me -e POSTGRES_DB=gitgud \
      -v gitgud-db:/var/lib/postgresql/data \
      docker.io/library/postgres:16-alpine
  2. 2 Write the configuration

    Run this once: it generates the secrets. Keep a copy of gitgud.env somewhere safe — GITGUD_SECRETS_MASTER_KEY encrypts CI secrets and two-factor keys, and without it they're gone.

    (umask 077; cat > gitgud.env <<EOF
    PHX_HOST=git.example.com
    DATABASE_URL=ecto://gitgud:change-me@gitgud-db/gitgud
    SECRET_KEY_BASE=$(openssl rand -base64 48 | tr -d '\n')
    GITGUD_RUNNER_JWT_SECRET=$(openssl rand -base64 48 | tr -d '\n')
    GITGUD_SECRETS_MASTER_KEY=$(openssl rand -base64 32)
    GIT_REPOS_PATH=/data/repos
    STORAGE_DISK_ROOT=/data/storage
    SSH_SERVER_ENABLED=true
    SSH_PORT=2222
    SSH_HOST_KEY_DIR=/data/ssh
    REGISTRATION_MODE=open
    EOF
    )
  3. 3 Migrate and start

    The image runs as nobody, so the data volume is handed to it first. Git over SSH is on port 2222; the web side stays on localhost for the proxy below.

    podman run --rm --user root -v gitgud-data:/data ggr.neiam.co/neiam/gitgud:latest chown nobody /data
    podman run --rm --network gitgud --env-file gitgud.env -v gitgud-data:/data ggr.neiam.co/neiam/gitgud:latest /app/bin/migrate
    podman run -d --restart unless-stopped --network gitgud --name gitgud --env-file gitgud.env -v gitgud-data:/data \
      -p 127.0.0.1:4000:4000 -p 2222:2222 \
      ggr.neiam.co/neiam/gitgud:latest
  4. 4 Put HTTPS in front

    The forge serves plain HTTP on port 4000 and expects to be reached as https://git.example.com. Any reverse proxy works; Caddy gets the certificate itself.

    caddy reverse-proxy --from git.example.com --to 127.0.0.1:4000
  5. 5 Make yourself the admin

    Register at https://git.example.com/users/register as you@example.com. With no SMTP_RELAY configured, mail is written to the log instead of sent — the sign-in link is in `podman logs gitgud`. Then:

    podman exec gitgud /app/bin/git_gud eval 'GitGud.Release.promote_admin("you@example.com")'
  6. 6 Close registration

    Sign-ups were open for you. Back to the default — invite only — and restart:

    sed -i '/^REGISTRATION_MODE=/d' gitgud.env && podman rm -f gitgud && \
      podman run -d --restart unless-stopped --network gitgud --name gitgud --env-file gitgud.env -v gitgud-data:/data \
      -p 127.0.0.1:4000:4000 -p 2222:2222 ggr.neiam.co/neiam/gitgud:latest

From here

  • Mail: set SMTP_RELAY, SMTP_USERNAME, SMTP_PASSWORD and MAIL_FROM_ADDRESS so sign-in links reach people instead of the log — where anyone who can read it can use them.
  • CI: register a forgejo-runner from a repository's or organization's Actions settings — the page has a snippet per runtime.
  • Behind a proxy that isn't on a private address, set GITGUD_TRUSTED_PROXIES so rate limits see clients rather than the proxy.
  • Everything else — storage backends, federation, package registries, Kubernetes — is in the README.