chadtmiller.com/knotless
Git hosted in your atproto account, on any stock PDS, fast enough for agents.
git clone knotless://chadtmiller.com/knotlessneeds the knotless git helper. set it up
# knotless-serve and its pages, as /etc/knotless/docker-compose.yml.
# deploy.sh copies it there, beside the .env it reads; edit it here.
#
# TLS is left to a reverse proxy in front: it joins the network named by
# KNOTLESS_PROXY_NETWORK and proxies the domain to knotless-serve:2590.
name: knotless
services:
serve:
image: knotless:current
restart: always
command:
- knotless-serve
- --listen
- 0.0.0.0:2590
- --public-url
- https://${KNOTLESS_DOMAIN:?set KNOTLESS_DOMAIN in .env}
- --index
- /data/index.db
- --jetstream
- ${KNOTLESS_JETSTREAM:-wss://jetstream.us-east.bsky.network}
- --web
- http://pages:2591
environment:
# The gateway's OAuth client key and the sessions owners delegate
# are in the config directory; mirrors and identities in the cache.
KNOTLESS_CONFIG_DIR: /data/config
KNOTLESS_CACHE_DIR: /data/cache
env_file:
# KNOTLESS_ADMIN_PASSWORD, made on the host by deploy.sh.
- /etc/knotless/secrets.env
volumes:
# Owned by uid 1000, the node user the image runs as.
- ${KNOTLESS_DATA:-/var/lib/knotless}:/data
networks:
default: {}
# The name the reverse proxy reaches it by.
proxy:
aliases: [knotless-serve]
depends_on:
- pages
deploy:
resources:
limits:
cpus: "2"
memory: 2g
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:2590/healthz').then(r => process.exit(r.ok ? 0 : 1), () => process.exit(1))"]
interval: 30s
timeout: 5s
retries: 3
pages:
image: knotless:current
restart: always
command: ["node", "/app/build"]
environment:
HOST: 0.0.0.0
PORT: "2591"
KNOTLESS_SERVE: http://serve:2590
# knotless-serve hands on the host and scheme it was asked for.
HOST_HEADER: x-forwarded-host
PROTOCOL_HEADER: x-forwarded-proto
deploy:
resources:
limits:
cpus: "1"
memory: 512m
networks:
proxy:
name: ${KNOTLESS_PROXY_NETWORK:-knotless}
external: true