shrugs is an Apache licensed self hosted git server available as a docker container written in Erlang and Rust.
Sometimes you just want to host some local changes before publishing to the cloud, using a simple git server than can easily run on something as small as a Raspberry PI.
If your public keys are on GitHub you can use (where username
is
your GitHub username):
docker run \
--name shrugs \
-e SSH_PUBLIC_KEY_URL="https://github.com/username.keys" \
-p 22022:22 \
ghcr.io/shortishly/shrugs
If your public keys are on GitLab you can use (where username
is
your GitLab username):
docker run \
--name shrugs \
-e SSH_PUBLIC_KEY_URL="https://gitlab.com/username.keys" \
-p 22022:22 \
ghcr.io/shortishly/shrugs
When SSH_PUBLIC_KEY_URL
is supplied, shrugs will fetch and apply the
keys found at the URL.
Alternatively you can run shrugs
and follow the instructions for
Authentication
below:
docker run \
--name shrugs \
-d \
-p 22022:22 \
ghcr.io/shortishly/shrugs
The container supports both amd64
and arm64
architectures.
To copy your authorized_keys
into the container for authentication:
docker cp ~/.ssh/authorized_keys shrugs:/users
Or copy the public key for individual users:
docker cp bob.pub shrugs:/users
docker cp alice.pub shrugs:/users
New keys ("*.pub" or "authorized_keys") in the users directory will be picked up (by default) every 5 seconds.
Push any repository (including a new repository) into shrugs with:
mkdir demo
cd demo
git init .
echo "world" > hello.txt
git add hello.txt
git commit --message='demo'
git branch -M main
git remote add origin ssh://localhost:22022/demo
git push --set-upstream origin main
Clone an existing repository already in shrugs with:
git clone ssh://localhost:22022/demo abc
Add some more content and push to the existing repository:
cd abc
echo "God’s Own Country" > yorkshire.txt
git add yorkshire.txt
git commit --message='home'
git push origin main
To list (ls) the repositories stored in shrugs:
$ ssh -p 22022 localhost ls
demo
With the addition of a host entry for shrugs in your .ssh/config
:
host shrugs
hostname localhost
checkhostip no
stricthostkeychecking no
loglevel QUIET
port 22022
Results in a bit less typing:
$ ssh shrugs ls
demo
$ git clone ssh://shrugs/demo
Cloning into 'demo'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (6/6), 466 bytes | 155.00 KiB/s, done.
$ cd demo
$ git remote -v
origin ssh://shrugs/demo (fetch)
origin ssh://shrugs/demo (push)
An example compose.yaml
with volume mounts for /repos
(git
repository storage), /etc/ssh
(shrugs ssh host key), and /users
(user public keys).
---
services:
shrugs:
image:
${SHRUGS_IMAGE:-ghcr.io/shortishly/shrugs:${SHRUGS_VERSION:-latest}}
ports:
- 22022:22
pull_policy:
${PULL_POLICY:-always}
environment:
SHRUGS_KEY_STORE_TRACE: false
SHRUGS_SSH_DAEMON_TRACE: false
SHRUGS_USERS_TRACE: false
volumes:
- repos:/repos
- host_keys:/etc/ssh
- user_keys:/users
volumes:
repos:
driver: local
host_keys:
driver: local
user_keys:
driver: local
With the above you can copy keys with docker compose cp rather than docker cp:
docker compose cp bob.pub shrugs:/users
docker compose cp alice.pub shrugs:/users
Shrugs uses the following directories in the container:
Directory | Description |
---|---|
/users | User public SSH keys in "*.pub" and/or "authorized_keys" |
/repos | Storage of git repositories |
/etc/ssh | SSH Host keys for Shrugs service |
The following environment variables can be used for configuration:
Variable | Default | Description |
---|---|---|
SHRUGS_SSHD_PORT | 22 | Incoming SSH connections on this port |
SHRUGS_AUTHENTICATION_ENABLED | true | "false" will disable user authentication |
SHRUGS_SYSTEM_DIR | /etc/ssh | This directory to find the host key |
SHRUGS_USER_DIR | /users | This directory to find authorized user keys |
SHRUGS_REPO_DIR | /repos | This directory to store git repositories |
SHRUGS_BIN_DIR | /bin | This directory to find the git executable |
SHRUGS_WATCH_TIMEOUT | 5000 | Check the repo dir every 5000ms for new user keys |
SHRUGS_INITIAL_BRANCH | main | Initial branch name used with git --init --bare |
The following environment variables can be used to enable debug logging:
Variable | Default | Description |
---|---|---|
SHRUGS_KEY_STORE_TRACE | false | Enables logging options from the key store |
SHRUGS_SSH_DAEMON_TRACE | false | Enables logging of the SSH daemon |
SHRUGS_USERS_TRACE | false | Enables logging of the users subsystem |
Erlang/OTP 25 and Rust are required.
make
A local docker container can be built with:
bin/build
Return a list of Erlang/OTP applications with versions running within the container:
ssh shrugs which_applications
[{sync,"Sync - Automatic Code Reloader","0.4.1"},
{syntax_tools,"Syntax tools","3.1"},
{compiler,"ERTS CXC 138 10","8.3.2"},
{shrugs,"Secure sHell Remote User Git Server","0.2.0-1-gcbbfc0d"},
{grimsby,"Erlang/Rust Port Manager","0.2.0"},
{envy,"wrapper prefixing os_env with application name","rolling"},
{any,[],"rolling"},
{ssh,"SSH-2 for Erlang/OTP","5.0.1"},
{public_key,"Public key infrastructure","1.14"},
{asn1,"The Erlang ASN1 compiler version 5.1","5.1"},
{sasl,"SASL CXC 138 11","4.2.1"},
{crypto,"CRYPTO","5.2"},
{stdlib,"ERTS CXC 138 10","5.0.2"},
{kernel,"ERTS CXC 138 10","9.0.2"}].
Return a list of comment lines from the public keys loaded into shrugs:
ssh shrugs auth_key_comments
["peter.james.morgan@gmail.com"].
Return the running Erlang/OTP version:
ssh shrugs system_version
Erlang/OTP 26 [erts-14.0.2] [source] [64-bit] [smp:4:4] [ds:4:4:10]