11 KiB
[17may2026] setting up self-hosted git
#
#
#
#
#
#
#
Problem
Github increasingly flaky, want to have an alternative.
Plan
Setup git hosting on a rented droplet.
Ingredients:
| DNS | register.com | conybeare.us |
| host | digitalocean.com | 159.203.164.149 |
DNS
-
login to
register.comaccount. drop register.com's nameservers, replace them with digital ocean'sNS ns1.digitalocean.com NS ns2.digitalocean.com NS ns3.digitalocean.com -
login to
digitalocean.comconsole, under Networking add similar domain records alogn with anArecordsA conybeare.us 159.203.164.149 NS conybeare.us directs to ns1.digitalocean.com NS conybeare.us directs to ns2.digitalocean.com NS conybeare.us directs to ns3.digitalocean.com I created record at domain root, so everything forwards to the chosen address
-
confirm working
$ nslookup conybeare.us Server: 127.0.0.53 Address: 127.0.0.53#53 Non-authoritative answer: Name: conybeare.us Address: 159.203.164.149
Nix
Setup nixpkgs.
# on 159.203.164.149
$ sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install) --daemon
We will also want system services to be able to use flakes
# /etc/nix/nix.conf build-users-group = nixbld trusted-users = root @wheel experimental-features = nix-command flakes # <-- adding this
Forgejo
Forgejo user
Add user
$ sudo useradd --system --shell /bin/false --home /var/lib/forgejo forgejo
$ sudo mkdir -p /var/lib/forgejo /var/log/forgejo
$ sudo mkdir -p /var/lib/forgejo/lfs
$ sudo mkdir -p /var/lib/forgejo/gitea-repositories
$ sudo chown -R forgejo:forgejo /var/lib/forgejo /var/log/foregejo
Forgejo setup
$ sudo mkdir -p /etc/forgejo
create /etc/foregejo/app.ini:
[server] ROOT_URL = https://conybeare.us/git/ APP_DATA_PATH = /var/lib/forgejo HTTP_ADDR = 127.0.0.1 HTTP_PORT = 3000 START_SSH_SERVER = true SSH_LISTEN_PORT = 2222 SSH_USER = forgejo SSH_PORT = 2222 [database] DB_TYPE = sqlite3 PATH = /var/lib/forgejo/forgejo.db [repository] ROOT = /var/lib/forgejo/gitea-repositories [ui] DEFAULT_THEME = forgejo-dark INSTANCE_SLOGAN = Mad Science, all the way down [lfs] PATH = /var/lib/forgejo/lfs STORAGE_TYPE = local [log] ROOT_PATH = /var/log/forgejo MODE = file LEVEL = info [mailer] ENABLED = false
$ sudo chown forgejo:forgejo /etc/forgejo/app.ini
$ sudo chmod 600 /etc/forgejo/app.ini
Runtime environment for forgejo
We want to use nix for this:
# /etc/forgejo/nix/flake.nix
{
description = "Forgejo environment";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system :
let pkgs = nixpkgs.legacyPackages.${system};
in {
packages.default = pkgs.forgejo;
apps.forgejo = {
type = "app";
program = "${pkgs.forgejo}/bin/forgejo";
};
devShells.default = pkgs.mkShell {
buildInputs = [ pkgs.forgejo ];
};
}
);
}
To verify syntax (will also fetch all the deps)
$ nix flake show /etc/forgejo/nix
$ ls /etc/forgejo/nix/flake.lock # now pinned
Systemd setup (Forgejo)
[Unit] Description=Forgejo After=network.target [Service] Type=simple User=forgejo Group=forgejo WorkingDirectory=/var/lib/forgejo ExecStart=/nix/var/nix/profiles/default/bin/nix run /etc/forgejo/nix#forgejo -- -c /etc/forgejo/app.ini web Restart=always RestartSec=5 Environment="HOME=/var/lib/forgejo" [Install] WantedBy=multi-user.target
then setup
$ sudo systemctl daemon-reload
$ sudo systemctl enable forgejo
$ sudo systemctl start forgejo
Verify status
$ sudo systemctl status forgejo
$ sudo journalctl -u forgejo -n 3
May 18 02:16:21 vpn1 nix[1043231]: Listen: http://127.0.0.1:3000
May 18 02:16:21 vpn1 nix[1043231]: AppURL(ROOT_URL): https://conybeare.us/
May 18 02:16:21 vpn1 nix[1043231]: Starting new Web server: tcp:127.0.0.1:3000 on PID: 1043231
Proxy Magic
Certificates
$ sudo /nix/var/nix/profiles/default/bin/nix run nixpkgs#certbot -- certonly --standalone -d conybeare.us
$ tree /etc/letsencrypt
/etc/letsencrypt/
├── accounts [error opening dir]
├── archive [error opening dir]
├── live [error opening dir]
├── renewal
│ └── conybeare.us.conf
└── renewal-hooks
├── deploy
├── post
└── pre
Nginx user
Add user
$ sudo useradd --system --shell /bin/false --home /var/cache/nginx nginx
Also working directories
$ sudo mkdir -p /var/cache/nginx
$ sudo chown nginx:nginx /var/cache/nginx
$ sudo chmod 755 /var/cache/nginx
$ sudo mkdir -p /var/log/nginx
$ sudo chown nginx:nginx /var/log/nginx
$ sudo chmod 755 /var/log/nginx
Nginx
/etc/nginx/nginx.conf:
events { }
http {
server {
listen 80;
server_name conybeare.us:
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name conybeare.us;
ssl_certificate /etc/letsencrypt/live/conybeare.us/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/conybeare.us/privkey.pem;
location /git/ {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
Runtime environment for nginx
Using nix again:
/etc/nginx/nix/flake.nix:
{
description = "Nginx environment";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let pkgs = nixpkgs.legacyPackages.${system};
in {
packages.default = pkgs.nginx;
apps.nginx = {
type = "app";
program = "${pkgs.nginx}/bin/nginx";
};
}
);
}
To verify syntax (and fetch deps)
$ nix flake show /etc/nginx/nix
$ ls /etc/nginx/nix/flake.lock
Systemd setup (Nginx)
/etc/systemd/system/nginx.service:
[Unit] Description=Nginx After=network.target [Service] Type=forking # User=nginx # start as root, bind to ports 80/443; then drop to nginx user for workers ExecStart=/nix/var/nix/profiles/default/bin/nix run /etc/nginx/nix#nginx -- -c /etc/nginx/nginx.conf ExecReload=/nix/var/nix/profiles/default/bin/nix run /etc/nginx/nix#nginx -- -c /etc/nginx/nginx.conf -s reload ExecStop=/nix/var/nix/profiles/default/bin/nix run /etc/nginx/nix#nginx -- -s quit [Install] WantedBy=multi-user.target
then ssetup
$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx
$ sudo systemctl status nginx
$ systemctl status nginx
● nginx.service - Nginx
Loaded: loaded (/etc/systemd/system/nginx.service; disabled; preset: enabled)
Active: active (running) since Mon 2026-05-18 04:13:51 UTC; 4s ago
Process: 1045381 ExecStart=/nix/var/nix/profiles/default/bin/nix run /etc/nginx/nix#nginx -- -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
Main PID: 1045385 (nginx)
Tasks: 2 (limit: 9489)
Memory: 2.9M (peak: 145.4M)
CPU: 1.281s
CGroup: /system.slice/nginx.service
├─1045385 "nginx: master process /nix/store/6xi5jqgw6mqdvzk36285ywc17wnx1548-nginx-1.30.1/bin/nginx -c /etc/nginx/nginx.conf"
└─1045386 "nginx: worker process"
May 18 04:13:50 vpn1 systemd[1]: Starting nginx.service - Nginx...
May 18 04:13:51 vpn1 nix[1045381]: nginx: [warn] the "listen ... http2" directive is deprecated, use the "http2" directive instead in /etc/nginx/nginx.conf:11
May 18 04:13:51 vpn1 systemd[1]: Started nginx.service - Nginx.
Enable-on-boot
$ sudo systemctl enable nginx forgejo
Testing
$ curl https://conybeare.us
<!DOCTYPE html>
<html lang="en-US" data-theme="forgejo-auto">
...
Forgejo install (web)
Now setup forgejo by visiting https://conybeare.us
| Instance Slogan | It's mad science, all the way down |
| Repository root path | /var/lib/forgejo/gitea-repositories |
| Git LFS root path | /var/lib/forgejo/lfs |
| Log path | /var/log/forgejo |
| Administrator Account | username, email, … |