Skip to main content
Version: Next (nightly)

Install

Two files, a folder, and Docker. The setup builder writes both files for you. You download them, run one command, and you're up. No cloning, no building. The hardware bar is modest: see what you need.

Two ways to start​

  • The fast path. Run over plain HTTP and you're using Cobblr in about five minutes. One limitation: scanning from your phone requires HTTPS, so the camera is off until you add it. Adding it later is a two-line edit to the same install, and nothing gets redone.
  • Right the first time. If you'd rather never touch it twice, set up Tailscale before you install (roughly 30 minutes if it's new to you), pick Tailscale in the builder instead of HTTP, and you arrive with HTTPS, the phone camera, and remote access on day one.

Both paths are the same two files and the same command. The only difference is the HTTPS choice you make in the builder, and it's never final.

The fast path: install over HTTP​

  1. Generate your files. Open the setup builder. Fill in your email, and under the HTTPS choice pick HTTP (no TLS). Set the address to your box's LAN IP and port, like 192.168.1.50:8088. The builder generates every secret for you. Download .env and docker-compose.yml into one folder on the box.

    Back up the encryption key

    The builder makes you confirm you've saved TENANT_CREDS_ENCRYPTION_KEY before the download unlocks. Keep that copy off this box. If you lose it, stored integration credentials can't be recovered.

    Keep data on a NAS or a data disk

    The builder's Advanced section has a Data location field. Set it and every mount (database, uploaded files, installed modules, TLS certs) lands under that one path, so Cobblr's data can live on a NAS mount or an appdata root instead of beside the compose file. Leave it blank to keep ./data. See Configuration.

  2. Bring it up:

    docker compose up -d

    Docker pulls the images (a few minutes the first time) and starts them. Nothing builds.

  3. Open it at http://<your-box-ip>:8088 from any device on your network, or at http://localhost:8088 on the box itself. Create your account (signup is open on first run) and look around.

You now have a working instance, with the one limitation from the fork above:

The phone camera requires HTTPS

Browsers only allow the camera (barcode scanning, photos) over HTTPS or on localhost. Scanning works in the browser on the box at localhost:8088, but not from your phone over http://192.168.x.x. It's a browser rule, and nothing in Cobblr overrides it. The next section fixes it in place.

Turn on HTTPS​

Pick one of the three options below. This section is both the upgrade from the fast path and the first stop on the do-it-right path, and the steps call out where the two differ.

  • Tailscale, which we suggest. A real certificate with nothing exposed. If you don't already have it (or another solution to this problem), it's worth the roughly 30 minutes to set up, and as a bonus you can then reach any of your self-hosted things securely from anywhere, not just Cobblr.
  • Cloudflare: a domain you own, on the open internet, which means forwarding a port.
  • Offline CA: no domain, no internet, no tailnet. You trust one certificate per device.
Why HTTPS on a LAN takes one of these

HTTPS is what the phone camera requires, and depending on the option you pick here, secure remote access can come along with it.

The catch with a home box: a public certificate authority won't issue a certificate for a private IP, and a public name won't reach your box unless you open a port to the internet or run your own DNS. So getting real HTTPS onto your LAN takes one of the approaches below.

Tailscale puts your devices on a private mesh network with real, auto-renewed certificates. Nothing is forwarded, nothing is exposed, and the same URL works at home or away. There are two ways to ride it. The first is easier and gives the nicer URL.

Own hostname (easiest)​

Cobblr's bundled proxy joins your tailnet by itself and serves the address you choose: the first part of the address becomes the node's name, so cobblr.<your-tailnet>.ts.net joins as cobblr, shop.<your-tailnet>.ts.net as shop. The box needs no Tailscale install at all. Only the devices you browse from do.

  1. Prepare the tailnet (one-time, in the admin console): enable MagicDNS and HTTPS certificates on the DNS page. That page also shows your tailnet name, the middle part of the address. Read it rather than assuming it looks like tail1234, because a tailnet created through a GitHub or Google login often does not.

  2. Build your files. In the builder, set the address to cobblr.<your-tailnet>.ts.net, pick Tailscale, and keep Own hostname. Leave the join set to Approve in browser (no key). Fresh install: download both files and bring them up as in fast-path step 2. Upgrading from the fast path: add these to your existing .env (keep your secrets) and remove the WEB_BIND line, then docker compose up -d:

    COBBLR_SITE_ADDRESS=cobblr.your-tailnet.ts.net
    COMPOSE_PROFILES=caddy
    COBBLR_TLS_MODE=tsnet
    The address must be a name this node actually owns

    Tailscale issues a certificate only for the node's own full name, and the proxy registers using only the first part of COBBLR_SITE_ADDRESS. Get the rest wrong and the node still joins, still shows Connected, and still looks healthy, while no certificate is ever issued and the browser fails with SSL_ERROR_INTERNAL_ERROR_ALERT.

    Two ways to land there:

    • The tailnet part is wrong. Copying the shape of an example and changing the digits produces a plausible name that is not yours.
    • The first part was already taken. If an earlier run left a cobblr node behind, the new one joins as cobblr-1, so cobblr.<your-tailnet> now belongs to something else.

    After the node appears, check its full name in the admin console against COBBLR_SITE_ADDRESS character for character.

  3. Approve the box. On first start it asks Tailscale to authorize itself and prints a one-time link in the logs. Grab it with docker compose logs caddy | grep -iE 'to authenticate|login.tailscale', open it, sign in, and approve the new node. This is the same click-to-approve you get authorizing any device, so there is no key to mint.

  4. Open it at your chosen address from any device signed into your tailnet (install the Tailscale app on your phone and laptop). The camera works everywhere now. One follow-up: find the new node in the admin console and disable key expiry on it, or it signs itself out in about six months.

Correcting the address later

Fixing only the tailnet part keeps the same node, because the proxy's state is keyed by the first part of the name and lives in a bind mount. Edit .env, run docker compose up -d caddy, and there is nothing to approve again. Changing the first part makes a different node, so a fresh approval link prints and you approve it once more. Worth knowing which you are doing, because being asked to approve a second time otherwise reads as something having gone wrong.

Prefer no link to click?

For a fully unattended box, mint a reusable auth key in the admin console (Settings, then Keys), choose Auth key (headless) in the builder, and paste it, or add TS_AUTHKEY=tskey-auth-... to the .env above. The box then joins on its own with no approval step. Everything else is identical.

The server's own name (tailscale serve)​

If the box is already on your tailnet (or you'd rather it be), skip the proxy and let the machine serve Cobblr under its own name, https://myserver.tail1234.ts.net:

  1. Put the box on the tailnet if it isn't: curl -fsSL https://tailscale.com/install.sh | sh && sudo tailscale up. Its name shows in tailscale status.
  2. In the builder pick Tailscale, then Server's name, with the machine's ts.net name as the address (upgraders: set that address, set COMPOSE_PROFILES= empty, remove WEB_BIND, keep your secrets), and bring it up. No page appears yet, since the app is loopback-bound until the next step.
  3. sudo tailscale serve --bg 8088 publishes it with a real certificate.

To reach either setup from outside your tailnet without forwarding a port, sudo tailscale funnel --bg 8088 (the serve variant) puts it on the internet over Tailscale's edge. Turn signup off first. Funnel must be enabled in your tailnet's ACL policy and serves only on ports 443, 8443, and 10000.

One box, many services?

The Own-hostname mode is exactly this pattern applied to Cobblr: a proxy joins the tailnet as a virtual node per service. If the box also runs other apps you'd like at their own ts.net names, the same idea generalizes with a standalone Caddy built with the tsnet plugin, one block per service. Outside this guide's scope.

Cloudflare (your own domain, on the open internet)​

If you own a domain on Cloudflare, choose Cloudflare in the builder and paste an API token scoped Zone → DNS → Edit for your zone. You get a real certificate through a DNS challenge, with a record like cobblr.example.com pointed at your box. This sets COBBLR_TLS_MODE=cloudflare and CLOUDFLARE_API_TOKEN.

The certificate is the easy half. Read the next section before picking this path, because reaching the box is where it actually gets decided.

What reaching your box from the internet takes​

The certificate needs no open port (the challenge happens over DNS), but the name points at your home's public IP, and three things stand between that and a working page:

  1. A port forward. On your router, forward TCP 443 (and 80 if you want the redirect) to the box's LAN IP. Every router hides this somewhere different, so search your router model plus "port forwarding". Give the box a fixed LAN IP first (a DHCP reservation), or the forward breaks when the lease changes.
  2. A real public IP. Many ISPs now put homes behind carrier-grade NAT, where no port forward is possible at all. The test: if the IP your router's status page shows differs from what whatismyip.com shows, you are behind CGNAT and this path cannot work. Use Tailscale instead.
  3. Hairpin NAT, for using it at home. From inside your own network the name resolves to your public IP, and many home routers refuse to loop that traffic back in. The tell: the page loads on mobile data but not on your own wifi. The fix is a local DNS entry on your router or Pi-hole mapping the name to the box's LAN IP (leave the public record alone, since many routers block public names that resolve to private addresses, as "DNS rebind protection").

An instance on the open internet takes login for everything, but close signup anyway: set PUBLIC_SIGNUP_ENABLED=false once your account exists.

If any of the three trips you and what you actually wanted was "reach it away from home", that is the Tailscale path above, which needs none of this.

If you set up with DuckDNS earlier

Older setups used a free DuckDNS name the same way (COBBLR_TLS_MODE=duckdns). That mode still works and your instance is fine, but it is no longer offered for new installs: the port-forward, CGNAT and hairpin realities above bit too many people, and nothing kept the DuckDNS record pointed at a home IP that changes. For internet access without owning a domain, use Tailscale (and tailscale funnel if it truly must be public).

Offline CA (no domain, no internet, no tailnet)​

Choose Offline CA in the builder (COBBLR_TLS_MODE=internal). Caddy runs its own certificate authority and issues the certificate locally, so nothing leaves the box. The cost is trusting that CA once per device. After the stack is up, export the CA:

docker compose exec caddy cat /data/caddy/pki/authorities/local/root.crt > cobblr-root.crt

On iOS, install the profile and enable full trust under Settings → General → About → Certificate Trust Settings. On Android, use Settings → Security → Install a certificate → CA certificate. This is the only path that needs no network and no third party at all.

Cobblr runs without it and will never bill you for one, but scanning and Cobb get noticeably weaker without it. Connecting one costs nothing and takes about five minutes:

  1. Go to aistudio.google.com and sign in with a Google account you already have.
  2. Create an API key. It is free: no card, and no Google Cloud project.
  3. Copy the key.
  4. In Cobblr, open Configuration → AI, add a provider, and pick Google AI Studio. Paste the key.
  5. Set the model to gemini-flash-lite-latest, exactly as written.

That's it. Scanning starts naming things on the next photo.

What gets weaker without AI

Two of the best parts suffer. Scanning falls back to barcodes and keyword matching, so anything without a barcode arrives unnamed and you fill in the details by hand. And Cobb, the assistant, has far less it can do for you.

Choosing a provider covers the free plan's limits and what else you could use, including a model on your own hardware if the box can run one, so nothing leaves your network.

Updating​

docker compose pull && docker compose up -d. Details, backups, and troubleshooting are in Operating.

Once you're in, Your first hour picks up inside the app.