# Self-hosting

> Run your own knot, the server that stores git repositories and their repo accounts.

Run your own knot to keep your repositories on a server you control. The web app still shows them, and people can still open issues and pull requests against them. The knot is a single Rust binary, `knot-server`, with no database: git is the only store, plus a sealed key file. [Run a knot](/docs/self-hosting/knot/) is the short version of this page.

If you already run the Go knot, follow [Golang to Rust knot migration guide](/docs/upgrading/knot-2/) instead.

## Requirements

* A Linux machine that stays online.
* A DNS name. It becomes the knot’s identity, `did:web:<hostname>`, so choose one you’ll keep.
* Your DID, which becomes the first admin.
* TLS, from a reverse proxy or from the knot itself.

## Master key and sealed keys

The knot keeps its signing keys in a sealed file, encrypted with a master key from an environment variable. Make the key once:

```sh
printf 'KNOT_MASTER_KEY=%s\n' "$(openssl rand -base64 32)" > knot.env
```

> **Caution**
>
> Back up the master key away from the knot. Without it, the knot loses control of every repository’s identity.

## SSH port

The knot runs its own SSH server on port 2222. To use port 22, move the system `sshd` first, or bind the knot to a second IP address. The [Rust based knot README](https://tangled.org/did:plc:j5hmlfdrwkvtxm7cjmu7j2is/blob/master/knot2/README.md) shows how.

## Configuration

```sh
knot-server config-template > config.toml
knot-server --config config.toml validate
```

The required keys are `server.hostname`, `server.admins`, `server.ssh_host_key_file`, `repo.scan_path`, `secrets.sealed_key_file`, `secrets.master_key_env` and `atproto.plc_directory`. Any key can also come from a `KNOT_*` environment variable.

> **Note**
>
> New repositories use SHA-256. If your users mirror from SHA-1 hosts such as GitHub, set `git.object_format = "sha1"` before anyone creates a repository.

## Install on NixOS

```nix
{
  imports = [ tangled.nixosModules.knot-rs ];


  services.tangled.knot-rs = {
    enable = true;
    environmentFile = "/etc/knot/knot.env";
    settings = {
      server = {
        hostname = "knot.example.com";
        admins = [ "did:plc:yourdid" ];
      };
      atproto.plc_directory = "https://plc.directory";
    };
  };
}
```

The knot listens on `127.0.0.1:5555` and keeps its state in `/var/lib/knot`.

## Install with a container

Use `atcr.io/tangled.org/knot:2`. It runs as uid 1000, so `chown` your data directories to `1000:1000`, and set `KNOT_SCAN_PATH`, `KNOT_SEALED_KEY_FILE` and `KNOT_SSH_HOST_KEY_FILE` to your mounted paths. [Run a knot](/docs/self-hosting/knot/#run-with-docker) has a full compose file.

## TLS

**Behind a reverse proxy.** Proxy to port 5555, and set `xrpc.trusted_proxy_header` and `xrpc.trusted_proxies` so rate limits apply per client. Don’t add CORS headers at the proxy.

**In the knot.** Set `tls.acme_enabled = true` and listen on 443. This is the only way to get HTTP/3.

## First run

```sh
curl https://knot.example.com/xrpc/_health
```

Then add the knot under Settings → Knots in the web app.

## Members and invites

By default only admins can create repositories. Set `acl.admission = "open"` to let anyone in, or add members with the admin commands. These need `acl.legacy_admin_secret_env` set:

```sh
knot-server --config config.toml admin member add did:plc:friend
knot-server --config config.toml admin create-invite-code
```

## Git LFS

We use the now-required `lfs.store_path` to have the knot save LFS objects, atproto record attachments (from issues and pulls, say) and soon LOPs objects too. Suggestion: choose a directory that’s alongside the repos folder, like `/var/lib/knot/lfs` beside `/var/lib/knot/repos` for example. If you don’t plan to have actual git repos using this for massive files, then it’ll be fine on the same disk as the git repos; if you *do* plan to have massive files, consider putting the LFS folder on a different disk entirely like an HDD. Wherever you put the folder, keep it out of the `repo.scan_path`.

## Updates

Stop the knot, replace the binary or image, and start it again. Read [Upgrading knots and spindles](/docs/upgrading/knots-and-spindles/) first.

## Troubleshooting

* The knot won’t start: run `knot-server validate` with the same config and environment.
* A push is rejected for an SSH key: publish the key on the user’s PDS, then wait up to an hour for the knot’s cache.
* Every user is rate-limited together: set `xrpc.trusted_proxy_header` and `xrpc.trusted_proxies`.

[Knot Configuration](/docs/reference/knot-config/) lists every setting.