← BitPanel

Debian & Ubuntu

One command installs the panel and everything it drives. Re-running upgrades rather than duplicating, and nothing already present at a good version is touched.

Install

npx bitpanel install

Run it as your normal user, not root — it installs into $HOME and calls sudo only where a package needs it.

It is a shell script that installs system packages. That deserves a look before you run it:

npx bitpanel url | xargs curl -fsSL | less

Without npm

curl -fsSL https://raw.githubusercontent.com/yashthakur1/bitroot-panel/main/install.sh | bash

The same script. The npm package pins it to a released tag and installs the panel at that tag; this one always takes the tip of main.

Then: the browser does the rest

The installer finishes by printing a URL and a generated password. Open it and the setup wizard collects everything else — no editing files over SSH.

The URL it prints is the loopback one, which is the right one when the browser is on the same machine:

http://127.0.0.1:3210

Installed on a headless box over SSH — the usual case — open it from your own machine instead, using the server's LAN address or its hostname. Ask the server for the address:

hostname -I | awk '{print "http://" $1 ":3210"}'   # e.g. http://192.168.1.42:3210

http://hostname:3210 works too wherever the network resolves the name, and once Tailscale is up the tailnet address reaches it from anywhere. The panel listens on all interfaces on 3210 — the password is what stands in front of it, so do not put that port on the public internet.

After that, Config → Setup shows what this machine can and cannot do, and why. Each dependency is probed by asking the service rather than reading config, and says what stops working until it is configured. Credentials are entered there directly.

What it installs

  1. git, curl, nginx, netcat-openbsd, jq from apt

  2. Node 22 from NodeSource, and pm2 globally

  3. cloudflared from Cloudflare's own .deb

  4. Garage from the official release for your architecture, plus /etc/garage.toml with generated secrets

  5. PocketBase, and the superuser account the panel authenticates with. That account is created silently and never printed, so its email and password are written to ~/apps/pocketbase/.superuser (mode 600) — read them from there when you want to sign in to PocketBase's own admin UI on port 8090:

    cat ~/apps/pocketbase/.superuser
  6. The panel itself, built, with a generated password in .env

  7. The helper scripts into ~/bin

  8. A single-node Garage layout, then everything under pm2, registered with systemd

What it leaves to you

No Cloudflare or Tailscale credentials are generated. They are account-level secrets, and creating them silently would be worse than asking.

# private access from anywhere
curl -fsSL https://tailscale.com/install.sh | sh && sudo tailscale up

# public routes
cloudflared tunnel login
cloudflared tunnel create $(hostname)

cloudflared tunnel login needs a browser, and blocks until it gets one. It prints a dash.cloudflare.com URL, tries to open it, and then waits — there is no timeout and no prompt. On a headless server nothing opens, so copy that URL into a browser on your laptop or phone, sign in, and pick the zone. Only when you authorise it there does the command return, writing ~/.cloudflared/cert.pem. cloudflared tunnel create fails without that file, so it has to be second.

If it looks stuck, it is waiting for you. Ctrl-C and re-run it if the URL scrolled away.

The Cloudflare token needs four permissions, and the setup view lists them with what each is for:

ScopePermissionWhy
ZoneDNS · Editcreates the DNS record behind each route
ZoneCache Rules · Editthe edge cache rule for published buckets
ZoneCache Purge · Purgeclears the edge when you replace a file
AccountAccount Rulesets · Editcache rules live on the account, not the zone

The account-level one is the one people miss. A token with every Zone permission and none of that looks complete, and then a cache rule fails with a bare 403 the first time a bucket is published.

Ports

PortService
3210the panel
3900Garage S3 API
3902Garage website endpoint, for published buckets
3903Garage admin API, bound to 127.0.0.1 only
8090PocketBase, bound to 127.0.0.1 only
9000deploy webhook, for push-to-deploy pipelines

Only the panel, published buckets and the deploy webhook need to be reachable from outside. Keep the rest on loopback or the tailnet.

Installed before 0.1.8? Check port 3903. Those installs wrote api_bind_addr = "[::]:3903" into /etc/garage.toml, which is every interface, not loopback — so the Garage admin API, which creates buckets and mints S3 keys behind nothing but a bearer token, answered on the LAN while this table said it did not.

ss -lntp | grep 3903 tells you: 127.0.0.1:3903 is fixed, *:3903 is not. Config → Setup detects it and offers Bind admin API to 127.0.0.1, which rewrites the one line and restarts Garage; re-running the installer does the same. Ports 3900 and 3902 stay reachable either way — published buckets need them.

Updates

bitpanel@x.y.z installs the panel released as x.y.z — the installer checks out that tag rather than the tip of a branch. The panel then compares itself against the latest published release and can fetch, build and restart into it from Config → Setup.

Packages are published from CI through npm trusted publishing, so each carries a signed provenance attestation tying it to the commit it was built from. A panel installed from a branch says so rather than inventing a comparison.