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:
knotrunning as a dedicatedgituser- 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.