org-howto/articles/2026/05/setup-forgejo-at-digital-ocean.org

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

  1. login to register.com account. drop register.com's nameservers, replace them with digital ocean's

    NS ns1.digitalocean.com
    NS ns2.digitalocean.com
    NS ns3.digitalocean.com
  2. login to digitalocean.com console, under Networking add similar domain records alogn with an A records

    A 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

  3. 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, …