Skip to content

Build from source

Use this guide when you need to build Quipthread from source. For most self-hosted installations, run the published Docker image instead. It includes the migration CLI and does not require Go or Bun on the host.

  • A Linux server or VPS
  • A domain or subdomain pointing to it
  • Go 1.26.4 or newer and Bun installed (for building from source)
  • Linux AMD64 or ARM64 for the pinned Atlas migration CLI
  • curl, sha256sum, and permission to install Atlas at /usr/local/bin/atlas
git clone https://github.com/quipthread/quipthread
cd quipthread
# Build the dashboard and embed widget, then copy assets into backend/static/
bun install --frozen-lockfile
bun run build:assets:selfhosted
# Build the Go binary with embedded assets
cd backend
CGO_ENABLED=0 go build -tags=selfhosted,production -o quipthread .

The resulting quipthread binary includes the dashboard and embed widget — no separate static file server needed.

The backend requires Atlas v1.3.0 at /usr/local/bin/atlas, even when serving a database that is already migrated.

Download the pinned binary for your server: Linux AMD64 or Linux ARM64. See the Atlas installation documentation for background. Quipthread verifies this exact version and checksum, so use the pinned download rather than the latest release.

The commands below download the correct binary, verify it against the repository checksum, and install it. Run them from the backend directory on your Linux server:

(
set -eu
case "$(uname -m)" in
x86_64) arch=amd64 ;;
aarch64|arm64) arch=arm64 ;;
*) echo "Atlas requires Linux AMD64 or ARM64" >&2; exit 1 ;;
esac
artifact="atlas-linux-${arch}-v1.3.0"
checksum="$(awk -v key="linux/${arch}/${artifact}" '$2 == key { print $1 }' migration/atlas_cli.sha256)"
test -n "$checksum"
download_dir="$(mktemp -d)"
trap 'rm -rf "$download_dir"' EXIT
curl --fail --silent --show-error --location \
"https://release.ariga.io/atlas/${artifact}" --output "$download_dir/atlas"
printf '%s %s\n' "$checksum" "$download_dir/atlas" | sha256sum --check --status
sudo install -m 0555 "$download_dir/atlas" /usr/local/bin/atlas
/usr/local/bin/atlas version
)

Keep the Atlas version and checksum aligned with the checkout you build. The Docker image performs this installation for you.

Create a .env file in the backend directory and run the binary from that directory. Avoid a conflicting .env in the parent directory, which the loader reads first. At minimum:

# Required
JWT_SECRET=<output of: openssl rand -hex 32>
BASE_URL=https://comments.example.com
ALLOWED_ORIGINS=https://publisher.example.com
DATABASE_URL=./data/comments.db
TRUST_PROXY=true
# At least one auth provider
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...

See Environment Variables for the full list. For OAuth app setup, see OAuth Setup.

./quipthread
# Listening on :8080

Keep this process running with a service manager such as systemd. Its working directory must contain .env, and its service account needs write access to the database directory. Startup runs migrations automatically and stops if migration fails.

Proxy it behind Caddy or nginx to serve HTTPS. With TRUST_PROXY=true, restrict direct access to port 8080 so only your trusted proxy can reach it. Use TRUST_PROXY=false when accessing the app directly.

Caddy example:

comments.example.com {
reverse_proxy localhost:8080
}

Navigate to https://comments.example.com/login and sign in with GitHub, Google, or email (if enabled). The first user to sign in is automatically promoted to admin.

Once signed in you will be redirected to the dashboard at /dashboard/. Create a site record — you will receive a Site ID.

Add the snippet to any page where you want comments. Place the <div> where the widget should appear:

<div
id="comments"
data-site-id="YOUR_SITE_ID"
data-page-id="YOUR_PAGE_ID"
></div>
<script
src="https://comments.example.com/embed.js"
async
></script>

Replace YOUR_SITE_ID with the ID from the dashboard, and set data-page-id to a unique string per page (e.g. the post slug).

Open a page with the embed and you should see the comment widget. Post a test comment and check it under Moderation in the dashboard. A new commenter’s first comment waits for moderator approval.

You can also visit https://comments.example.com/dev to test the widget in isolation against the built-in dev test page.

From the repository root, build the image:

docker build -t quipthread:local .

Use quipthread:local in place of the published image in the Docker Compose or docker run example. The Docker build installs its own build tools.

Use the backup and restore guide for the Docker image. A directly installed Go binary does not start Litestream for you; configure your own SQLite backup process before using it in production.