Skip to content

Self-hosting

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 is the short version of this page.

If you already run the Go knot, follow Upgrading to Knot 2 instead.

  • 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.

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

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

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 knot 2 README shows how.

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.

{
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.

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 has a full compose file.

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.

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

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

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:

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

Set lfs.store_path to turn on Git LFS over SSH and HTTPS.

Stop the knot, replace the binary or image, and start it again. Read Upgrading knots and spindles first.

  • 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 lists every setting.