krz/gitbay

A CLI-first git forge.

clone: git clone https://gitbay.org/krz/gitbay.git

e1063b03005e952d4e50e0c504459005bd2dd642

verified · cmc

author: Christian Cleberg <hello@cleberg.net> · 2026-08-24T01:03:45Z

deploy: Vultr/Ubuntu 24.04 kit

- cloud-init: admin sshd moved to 2222 (sshd_config drop-in plus
  ssh.socket override — 24.04 socket-activates sshd), gitbay system
  user, hardened systemd unit with CAP_NET_BIND_SERVICE for 22/80/443,
  production config.toml (embedded SSH on 22, acme TLS, view_only,
  closed registration), nightly backup timer keeping 7, ufw for
  22/80/443/2222
- install.sh: scp the binary, check-config, restart, status
- dist/ (cross-compiled binaries) gitignored
 .gitignore             |   1 +
 deploy/cloud-init.yaml | 130 +++++++++++++++++++++++++++++++++++++++++++++++++
 deploy/install.sh      |  20 ++++++++
 3 files changed, 151 insertions(+)

diff --git a/.gitignore b/.gitignore
index a8cf6e3..c4b16e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 /gitbay
 /gitbayd
 *.db
+/dist/
diff --git a/deploy/cloud-init.yaml b/deploy/cloud-init.yaml
new file mode 100644
index 0000000..1df7c9a
--- /dev/null
+++ b/deploy/cloud-init.yaml
@@ -0,0 +1,130 @@
+#cloud-config
+# gitbay VPS bootstrap (Ubuntu 24.04).
+#
+# What this does on first boot:
+#   - moves the host's admin sshd to port 2222 (gitbay's embedded SSH
+#     listener owns port 22) — CONNECT ON 2222 AFTER FIRST BOOT
+#   - creates the unprivileged gitbay user and directory layout
+#   - installs /etc/gitbay/config.toml, the systemd unit (with
+#     CAP_NET_BIND_SERVICE so ports 22/80/443 work without root), and a
+#     nightly backup timer
+#   - opens ufw for 22, 80, 443, 2222
+#
+# It does NOT install the gitbayd binary (it is not hosted anywhere yet);
+# scp it to /usr/local/bin/gitbayd afterward and `systemctl start gitbayd`.
+
+package_update: true
+packages:
+  - git
+  - ufw
+
+write_files:
+  # Admin sshd on 2222. Ubuntu 24.04 socket-activates sshd, so the port
+  # must change in BOTH sshd_config and the socket unit.
+  - path: /etc/ssh/sshd_config.d/60-gitbay-port.conf
+    content: |
+      Port 2222
+      PasswordAuthentication no
+  - path: /etc/systemd/system/ssh.socket.d/override.conf
+    content: |
+      [Socket]
+      ListenStream=
+      ListenStream=2222
+
+  - path: /etc/gitbay/config.toml
+    permissions: "0640"
+    content: |
+      [server]
+      root = "/var/lib/gitbay"
+      site_url = "https://gitbay.org"
+
+      [ssh]
+      mode = "embedded"
+      port = 22
+
+      [http]
+      addr = ":443"
+      tls = "acme"
+      acme_email = "hello@gitbay.org"
+      acme_http_addr = ":80"
+
+      [web]
+      mode = "view_only"
+
+      [registration]
+      mode = "closed"
+
+  - path: /etc/systemd/system/gitbayd.service
+    content: |
+      [Unit]
+      Description=gitbay forge daemon
+      After=network-online.target
+      Wants=network-online.target
+
+      [Service]
+      User=gitbay
+      Group=gitbay
+      ExecStart=/usr/local/bin/gitbayd --config /etc/gitbay/config.toml serve
+      Restart=on-failure
+      RestartSec=3
+
+      # Bind 22/80/443 without root; no privilege escalation afterward.
+      AmbientCapabilities=CAP_NET_BIND_SERVICE
+      CapabilityBoundingSet=CAP_NET_BIND_SERVICE
+      NoNewPrivileges=yes
+      ProtectSystem=strict
+      ProtectHome=yes
+      ReadWritePaths=/var/lib/gitbay /var/backups/gitbay
+      PrivateTmp=yes
+      ProtectKernelTunables=yes
+      ProtectControlGroups=yes
+      RestrictSUIDSGID=yes
+
+      [Install]
+      WantedBy=multi-user.target
+
+  - path: /usr/local/bin/gitbay-backup.sh
+    permissions: "0755"
+    content: |
+      #!/bin/sh
+      # Nightly consistent backup; keeps the last 7 locally.
+      # To ship offsite, add an rclone/s3 upload of $out here.
+      set -eu
+      dir=/var/backups/gitbay
+      out="$dir/gitbay-$(date -u +%Y%m%d-%H%M%S).tar.gz"
+      /usr/local/bin/gitbayd --config /etc/gitbay/config.toml admin backup --out "$out"
+      ls -1t "$dir"/gitbay-*.tar.gz | tail -n +8 | xargs -r rm --
+
+  - path: /etc/systemd/system/gitbay-backup.service
+    content: |
+      [Unit]
+      Description=gitbay nightly backup
+      [Service]
+      Type=oneshot
+      User=gitbay
+      ExecStart=/usr/local/bin/gitbay-backup.sh
+
+  - path: /etc/systemd/system/gitbay-backup.timer
+    content: |
+      [Unit]
+      Description=gitbay nightly backup
+      [Timer]
+      OnCalendar=*-*-* 09:00:00 UTC
+      RandomizedDelaySec=15m
+      Persistent=true
+      [Install]
+      WantedBy=timers.target
+
+runcmd:
+  - adduser --system --group --home /var/lib/gitbay --shell /usr/sbin/nologin gitbay
+  - install -d -o gitbay -g gitbay -m 750 /var/lib/gitbay /var/backups/gitbay
+  - chgrp gitbay /etc/gitbay/config.toml /etc/gitbay
+  - ufw allow 22/tcp
+  - ufw allow 80/tcp
+  - ufw allow 443/tcp
+  - ufw allow 2222/tcp
+  - ufw --force enable
+  - systemctl daemon-reload
+  - systemctl restart ssh.socket || systemctl restart ssh
+  - systemctl enable gitbayd gitbay-backup.timer
+  - systemctl start gitbay-backup.timer
diff --git a/deploy/install.sh b/deploy/install.sh
new file mode 100755
index 0000000..62fe8be
--- /dev/null
+++ b/deploy/install.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+# Push the gitbayd binary to a freshly cloud-inited host and start it.
+# Usage: deploy/install.sh <host-or-ip> [ssh-port]
+set -eu
+host=${1:?usage: install.sh <host-or-ip> [ssh-port]}
+port=${2:-2222}
+bin=dist/gitbayd-linux-amd64
+
+[ -f "$bin" ] || { echo "build first: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o $bin ./cmd/gitbayd" >&2; exit 1; }
+
+scp -P "$port" "$bin" "root@$host:/usr/local/bin/gitbayd.new"
+ssh -p "$port" "root@$host" '
+  set -eu
+  chmod 755 /usr/local/bin/gitbayd.new
+  mv /usr/local/bin/gitbayd.new /usr/local/bin/gitbayd
+  /usr/local/bin/gitbayd --config /etc/gitbay/config.toml check-config --no-host-checks
+  systemctl restart gitbayd
+  sleep 1
+  systemctl --no-pager --lines=5 status gitbayd
+'