just-use-git image
A single Alpine-based image that runs a complete self-hosted git
server: SSH push/pull with on-demand create, update, and destroy
commands, and
read-only browsing/clone over HTTPS via cgit and Caddy. It's designed
to be used with no rebuild — every host-specific value is an
environment variable — and no dependency on jugctl at all; a
stranger can copy a quadlet snippet (or a plain podman run), set the
variables below, and have a working git server.
One image, three roles, one container (and one process) per role, all
sharing a pod so the roles can reach each other over 127.0.0.1:
| Role |
What it runs |
Published ports |
sshd |
OpenSSH + git-shell + create/update/destroy |
22 |
fcgi |
fcgiwrap (cgit and git-http-backend) |
none (127.0.0.1:9000, internal only) |
caddy |
TLS termination, HTTP clone, cgit UI |
80, 443 |
Run a role by passing it as the container's command, e.g.:
podman run -d --name just-use-git-sshd \
-e GIT_CLONE_HOST=git.example.com \
-e AUTHORIZED_KEYS_B64="$(base64 -w0 authorized_keys)" \
-p 22:22 \
-v repos:/srv/git -v ssh-keys:/srv/ssh \
docker.io/dickdavis/just-use-git:latest sshd
Environment variables
| Variable |
Roles |
Required |
Description |
GIT_CLONE_HOST |
sshd, caddy |
yes |
Hostname clients use in git@<host>:repo.git clone URLs and in the git vhost's own hostname/go-import responses. |
AUTHORIZED_KEYS_B64 |
sshd |
yes |
Base64-encoded authorized_keys file contents (one or more public keys) for the git user. Base64 avoids quoting problems passing multi-line key lists through quadlet Environment=. |
GIT_REPOS_ROOT |
sshd, fcgi, caddy |
no (default /srv/git) |
Path to the bare repos root. Also the git user's home, so short clone URLs resolve. |
GIT_DEFAULT_BRANCH |
sshd |
no (default main) |
Sets the git user's global init.defaultBranch, applied to repos created by create. |
CGIT_TITLE |
fcgi |
no (default just-use-git) |
cgit's root-title. |
CGIT_DESCRIPTION |
fcgi |
no (default empty) |
cgit's root-desc. |
WEB_HOST |
caddy |
yes |
Hostname Caddy serves the cgit browsing UI on. |
GIT_HOST |
caddy |
yes |
Hostname Caddy serves smart-HTTP clone and go-import discovery on. Set this to the same value as GIT_CLONE_HOST. |
ACME_EMAIL |
caddy |
yes |
Contact email Caddy registers with Let's Encrypt for automatic HTTPS. |
Volumes
| Volume |
Mounted by |
Path |
Contents |
| repos |
sshd (rw), fcgi (ro), caddy (ro) |
/srv/git |
Bare repositories. The only irreplaceable state — back it up, never delete it. |
| ssh host keys |
sshd |
/srv/ssh |
Generated on first start if absent; must persist so clients don't see host key warnings across restarts/upgrades. |
| caddy data |
caddy |
/srv/caddy |
ACME account keys and issued certificates, plus /srv/caddy/assets holding the customizable cgit static assets (css/logo/favicon), seeded from image defaults on first start; must persist to avoid re-hitting Let's Encrypt rate limits on every restart. |
Notes
create, update, and destroy are baked into the image at
/usr/local/lib/just-use-git/git-shell-commands/{create,update,destroy} and
symlinked into $GIT_REPOS_ROOT/git-shell-commands on every sshd
start, so an upgraded image always wins over whatever was there
before.
- Push over HTTP is never possible: the git vhost only routes
git-upload-pack, info/refs, HEAD, and objects/* to
git-http-backend; git-receive-pack is not routed.
- The image installs
git-daemon alongside git — that package is
what actually ships git-http-backend on Alpine, despite the name.