Setting Up Tangled on Alpine Linux

March 7, 2026

I've been exploring Tangled lately. Tangled is a decentralized, Git-based collaboration layer built on the AT Protocol: you host your own repos (knots), and the ecosystem treats them as first-class, verifiable data stores. If that sounds interesting, the Tangled site has the overview, docs, and source.

This guide walks through a clean Tangled knot setup on Alpine Linux (tested on Alpine 3.22). The final architecture is:

  • knot running as a dedicated git user
  • OpenRC managing the service lifecycle
  • Nginx reverse proxying HTTP and websocket traffic

Most commands below should be run as root unless a step explicitly says otherwise.

1) Update Alpine package indexes

Start by refreshing apk indexes so you install the latest package metadata available in your configured mirrors.

apk update

2) Install Go

Download and extract Go, then expose it through /etc/profile.d so all users can access the toolchain.

wget https://go.dev/dl/go1.26.1.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.26.1.linux-amd64.tar.gz
echo 'export GOROOT=/usr/local/go' >> /etc/profile.d/go.sh
echo 'export GOPATH=$HOME/go' >> /etc/profile.d/go.sh
echo 'export PATH=$GOROOT/bin:$GOPATH/bin:$PATH' >> /etc/profile.d/go.sh
. /etc/profile

Optional sanity check:

go version

3) Build the Knot binary

knot currently needs CGO, so we install the compiler toolchain (build-base) and enable CGO_ENABLED=1 before building.

apk add git build-base
git clone https://tangled.org/@tangled.org/core
cd core
export CGO_ENABLED=1
go build -o knot ./cmd/knot
mv knot /usr/local/bin/knot
chown root:root /usr/local/bin/knot

Optional sanity check:

/usr/local/bin/knot --help

4) Create and configure the git service user

Use a dedicated unprivileged user to run the service and own repositories.

adduser -D git

Switch to that user and define environment variables in /home/git/.knot.env:

su git
cat > /home/git/.knot.env <<'EOF'
KNOT_REPO_SCAN_PATH=/home/git
KNOT_SERVER_HOSTNAME=knot.example.com
APPVIEW_ENDPOINT=https://tangled.org
KNOT_SERVER_OWNER=did:plc:foobar
KNOT_SERVER_INTERNAL_LISTEN_ADDR=127.0.0.1:5444
KNOT_SERVER_LISTEN_ADDR=127.0.0.1:5555
EOF
exit

Notes on key values:

  • KNOT_SERVER_HOSTNAME: public DNS name users will connect to.
  • KNOT_SERVER_OWNER: your DID from the Tangled settings page.
  • KNOT_SERVER_LISTEN_ADDR: public-facing local listener (proxied by Nginx in this guide).
  • KNOT_SERVER_INTERNAL_LISTEN_ADDR: secondary local bind used by internal operations.

5) Configure SSH for key lookup

Knot can provide authorized SSH keys dynamically. Add this block to /etc/ssh/sshd_config:

cat >> /etc/ssh/sshd_config <<'EOF'
Match User git
    AuthorizedKeysCommand /usr/local/bin/knot keys -o authorized-keys
    AuthorizedKeysCommandUser nobody
EOF

Validate config before restart to avoid locking yourself out:

sshd -t
rc-service sshd restart

6) Create an OpenRC service for Knot

Create /etc/init.d/knotserver:

#!/sbin/openrc-run

name="knotserver"
description="Knot server"

command="/usr/local/bin/knot"
command_args="server"
command_user="git:git"

pidfile="/run/${RC_SVCNAME}.pid"

# log files
output_log="/var/log/${RC_SVCNAME}.log"
error_log="/var/log/${RC_SVCNAME}.err"

start_pre() {
    checkpath --directory --owner git:git --mode 0755 /var/log
    checkpath --file --owner git:git --mode 0644 "$output_log"
    checkpath --file --owner git:git --mode 0644 "$error_log"
    [ -f /home/git/.knot.env ] && . /home/git/.knot.env
}

start() {
    ebegin "Starting ${name}"
    [ -f /home/git/.knot.env ] && . /home/git/.knot.env

    set -a 
    . /home/git/.knot.env
    set +a

    supervise-daemon "${RC_SVCNAME}" \
        --start \
        --pidfile "${pidfile}" \
        --user "${command_user}" \
        --chdir /home/git \
        --stdout "${output_log}" \
        --stderr "${error_log}" \
        -- "${command}" ${command_args}

    eend $?
}

Make it executable, register for boot, and start it:

chmod +x /etc/init.d/knotserver
rc-update add knotserver default
rc-service knotserver start

Optional sanity checks:

rc-service knotserver status
ss -lntp | rg 5555

7) Configure Nginx as reverse proxy

Install Nginx and enable it at boot:

apk add nginx
rc-update add nginx default

Edit /etc/nginx/http.d/default.conf:

server {
    listen 80;
    listen [::]:80;
    server_name knot.example.com;

    location / {
        proxy_pass http://localhost:5555;
        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;
    }

    location /events {
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade websocket;
        proxy_set_header Connection Upgrade;
        proxy_pass http://localhost:5555;
    }

    # Add SSL/TLS settings here for production.
}

Validate Nginx config and restart:

nginx -t
rc-service nginx restart

8) Final checks and common issues

At this point the knot server should be reachable at knot.example.com (or your chosen hostname) via Nginx.

Register your knot from the Tangled knots settings page.

If you use a CDN in front (as I do), TLS is terminated there. Otherwise, configure TLS in Nginx before exposing the service.

If SSH logins as the git user fail, ensure the account is unlocked:

usermod -U git

Some setups treat “no password” as locked; if unlock alone isn’t enough, set a random password so the account is considered active:

usermod -p "$(openssl rand -base64 32 | openssl passwd -6 -stdin)" git

In /etc/ssh/sshd_config, add or confirm:

PasswordAuthentication no
PubkeyAuthentication yes

To troubleshoot errors, check the knot logs at /var/log/knotserver.log and /var/log/knotserver.err.