For all of the work one might do regarding self-sufficiency (or other personal projects), be that modifications to code, documents, images, or literally any modifiable process on a computer system, it is wise to use a version control system to keep track.

Version contol systems, like git, or fossil, allow for the set-up of trackers and managers that are able to present a history of changes, as they are logged.

This means that after a modification is made, it can be “commited” to the version control system, and stored. Effectively, you commit to the change.

Although it is relatively simple to set up a git server for self-hosting (via cgit perhaps), on its own it lacks an intuitive, easy to use way to interact with, apart from a command line interface. For this reason I personally use a Forgejo instance, which is the same software running in Codeberg

specs #

Forgejo is extremely lightweight, and on it barely registers any resource usage. As a nix service, it consumes around 139M of RAM on my system.

installation #

Create a forgejo folder and a default.nix file inside it, at the place where you have the rest of your nixos configuration

$ mkdir forgejo && touch forgejo/default.nix

(or use the file explorer of your choice)

nix declaration #

Open forgejo/default.nix in any text editor, and copy the following

{
  services = {
    forgejo = {
      enable = true;

      dump.enable = true;
      # useWizard = true;
      
      settings = {
        DEFAULT = {
          APP_NAME = "<A-COOL-INSTANCE-NAME>"; # Like "yourname's Forge"
          RUN_MODE = "prod";
          APP_SLOGAN = "<SOMETHING-COOL>"; # Try "Fuck Microsoft"
          RUN_USER = "forgejo";
        };

        server = {
          HTTP_PORT = 3040;
          DOMAIN = "forge.<YOUR-DOMAIN>";
          ROOT_URL = "http://forge.<YOUR-DOMAIN>/";
          SSH_DOMAIN = "forge.<YOUR-DOMAIN>";
          SSH_PORT = 22;
          DISABLE_SSH = false;
        };

        database = {
          DB_TYPE = "sqlite3";
        };

        service = {
          DISABLE_REGISTRATION = false;
          DEFAULT_KEEP_EMAIL_PRIVATE = true;
          REQUIRE_SIGNIN_VIEW = false;
          REGISTER_EMAIL_CONFIRM = false;
          ENABLE_NOTIFY_MAIL = false;
          ALLOW_ONLY_EXTERNAL_REGISTRATION = false;
          ENABLE_CAPTCHA = false;
          DEFAULT_ALLOW_CREATE_ORGANIZATION = true;
          DEFAULT_ENABLE_TIMETRACKING = true;
        };

        lfs = {
          enable = true;
        };
      };
    };

    nginx = {
      virtualHosts."forge.<YOUR-DOMAIN>" = {
        enableACME = true;
        forceSSL = true;

        locations."/" = {
          proxyPass = "http://127.0.0.1:3040";
          proxyWebsockets = true;
        };
      };
    };
  };
}

Then to enable it, just include it in the server config file:

  imports = [
    # ... other services
    ./forgejo
    # ... other services
  ];

and you’re done.

Let’s break the config file down.

explanation #

  1. First of all we declare the forgejo service as enabled.

  2. dump.enable enables backups, to be performed daily at 04:31 (4:31AM), and stored at /var/lib/forgejo/dump

  3. useWizard enables the Initial Configuration page on first launch. It allows for some non-nixo-style configuration, just in case.

  4. The settings define the configuration of forgejo itself:

    1. DEFAULT are the base options, such as the name, slogan, and type (production or development) of the instance
    2. server are server configuration options, which are options that will be written in the app.ini (the forgejo config file at run-time), under the [service] tag. You can find all of them here
      • We set the DOMAIN to be forge.<YOUR-DOMAIN> so that it apperas on git clone commands,
      • We set the HTTP_PORT to 3040 because it’s a non-standard port (the default of 3000 might clash with other services)
      • We set the HTTP_ADDR to 127.0.0.1 so that the service is not accessible to anyone outside of the machine, except via reverse proxy through nginx
    3. service deals with some of the more middleware things; here we have it set to allow registrations, so that we might make our own admin account. After that is done, it should be
      DISABLE_REGISTRATION = true; so so that only the admin (you) can create new users.
    4. We enable git LFS (Large File Storage) just in case.
  5. Finally, we declare an nginx virtual host to set up a reverse proxy to point forge.<YOUR-DOMAIN> to HTTP_PORT 3040, so that you can access forgejo via the web.

If you change the HTTP_PORT, remember to change it also in the nginx block.

In general the settings block works as follows:
Each of name = {} that starts a block within settings declares the equivalent [name] block in the non-default app.ini. Then, in that, it adds the items you define in the config. For example, assume we have this:

{
  services = {
    forgejo ={
      settings = {
        server = {
          HTTP_PORT = 3000;
        };
      }; 
    };
  };
}

this then translates inside of the app.ini file to

[server]
HTTP_PORT = 3000

once the setup is set up.

More information can be found at the wiki, but it can be opaque.
More options can be found here.
For example, if you plan to use this instance with more that ~4-5 people, it might be good to change the backend database from SQLite3 to PostgreSQL.
I leave the process of doing that as an exercise to the reader (Hint: It’s a single line inside of services.forgejo.database).

Make sure to update the parts inside of <>.
It should now appear in localhost:3010. If you are hosting this in a public server (like a VPS), it can be found in http://forge.<YOUR-DOMAIN>.

further setup #

Once the service is up and running, go to localhost:3010, or forge.<YOUR-DOMAIN>, or whatever domain you set it to.

You will see a setup page Initial Configuration.

You must set up an administrator account. This can either be a completely seperate account to your own, or it could be your own account.

You can also set up an email server using the SMTP address of the email provider, as well as check out other small configuration stuff you can do. Configuring things like this however beats the purpose of using NixOS. You can always copy app.ini and translate it into NixOS later anyway though.

If you do translate app.ini —> nix, make sure to not include any secrets (JWT/INTERNAL_TOKEN/etc).

If you don’t want to have registration open for everyone, remember to set
DISABLE_REGISTRATION = false;

backups #

code backups #

In general it is a good idea to have your code in multiple repositories. This allows you to not worry about your server going down, your code getting deleted, etc.

I use codeberg as a backup mirror for my personal work, and github for my work at αpothēke. Although I don’t like github, it is the best place for others to discover and contribute.

service backups #

Because we have service.forgejo.dump.enabled = true;, we can simply copy /var/lib/forgejo/dump to another computer. If you want to keep any updates and configurtions after running, and made outside of nix, you can simply make a copy of the entire /var/lib/forgejo folder at regular intervals.