HTTPS and access
HTTPS is what the phone camera requires, and it's the part of a home setup with the most moving pieces: a certificate, a name, and a route to the box. This page covers the failures specific to that. The setup itself, and which TLS option fits your situation, is in Install.
The phone camera is blocked
Fix: turn on HTTPS, or scan on the box itself at localhost.
- On the box itself,
http://localhost:8088counts as secure, so scanning works there. - From a phone, you need HTTPS. Turn it on with one of the options in Install. Tailscale is the recommended one because it gives every device a real certificate with nothing exposed.
- HTTPS on and the camera still won't open? Confirm the address bar shows
https://with no certificate warning. On the offline-CA option the certificate has to be trusted on the device, not only installed. On iOS that is the extra toggle under Settings, General, About, Certificate Trust Settings.
Why the camera needs HTTPS
Browsers only allow the camera (barcode scanning, photos) in a secure context,
which means HTTPS or localhost. Over a plain http://192.168.x.x LAN address
the camera silently doesn't open. No setting in Cobblr changes this, because
it's a browser rule.
Caddy crash-loops on first run
Fix: read docker compose logs caddy, which shows the reason. The HTTPS
proxy (caddy) exits on a bad configuration instead of serving without TLS.
- Unknown TLS mode. A line like
unknown COBBLR_TLS_MODE '...'means the value isn't one ofcloudflare,internal,tsnet, or the legacyduckdns. Fix the spelling in.env. The builder sets this for you. - A blank ACME email on an older image. The ACME modes carry an optional
contact email. When
COBBLR_ACME_EMAILwas blank, an older image rendered a bareemailline that Caddy rejected as a parse error, and the container crash-looped. Current images self-heal by dropping the empty line, and the builder fills the field from your email. If you're on an older image, either pull the current one (docker compose pull) or setCOBBLR_ACME_EMAILto any address you control.
Tailscale (tsnet) sits there and never comes up
Fix: open the one-time sign-in link in the logs and approve the node. Nothing is broken. It just hasn't been authorized yet.
- Find the link:
docker compose logs caddy | grep -iE 'to authenticate|login.tailscale'
- Open the
https://login.tailscale.com/...link that prints, sign in, and approve the new node. The address you set inCOBBLR_SITE_ADDRESSstarts answering right after, and the approval is one-time. - No such line? Follow the logs live (
docker compose logs -f caddy) while the container starts, since the link prints early. - Once the node is up, find it in the Tailscale admin console and disable key expiry on it, or it signs itself out in a few months and you get the same wait again.
Why it waits, and the headless alternative
On the own-hostname option the proxy joins your tailnet as its own node, and by
default it waits for you to approve it. No auth key is needed for that path.
If you would rather the box never wait on a link, put a reusable auth key from
the admin console in TS_AUTHKEY and it joins on its own. That is the optional
headless path, not a requirement.
The certificate didn't issue (DNS challenge)
Fix: check the DNS token and the box's outbound internet. The Cloudflare
mode (and the retired DuckDNS mode, on older installs) gets a real certificate
through a DNS challenge, and when the challenge fails,
docker compose logs caddy names it. The usual causes:
- A wrong
CLOUDFLARE_API_TOKEN(orDUCKDNS_TOKENon a legacy setup). The Cloudflare token must be scoped Zone, DNS, Edit for your zone. - No outbound internet from the box, so it can't reach Let's Encrypt.
Once a certificate issues it's cached under ./data/caddy, so this is a
first-run problem, not something that recurs on every restart.
A name resolves but the page won't load
Fix: add an exception for the name in your router or Pi-hole, or add a local DNS entry mapping the name straight to the box's LAN IP.
Why this happens: DNS rebind protection
Many routers and Pi-hole setups refuse to answer a public name (like
cobblr.example.com) that points at a private LAN IP, as a security measure.
The laptop on one network loads it, but the phone on another doesn't. This only
affects the internet-facing modes, since Tailscale doesn't use public DNS at all
and sidesteps it.
The public name reachable, Tailscale not (or the reverse)
These are different routes, and a device has to be on the right one.
- Tailscale. The device must be signed into the same tailnet, with the
Tailscale app running. Confirm the box is serving: on the own-hostname
(
tsnet) mode the proxy joins as its own node, so it appears in the Tailscale admin console. On the serve mode,tailscale serve statuson the box should list the app on127.0.0.1:8088. To reach a serve setup from outside the tailnet you needtailscale funnel, notserve. - A public (Cloudflare) name. This reaches the box over the public
internet, which requires the port forward on your router (TCP 443) to
actually be in place and the name to point at your current public IP. Because
they put the instance on the internet, set
PUBLIC_SIGNUP_ENABLED=falseand rely on login (see Accounts and access).
Locked out: signup is closed and nobody can get in
Fix: one .env edit. Because you own the box, this is always recoverable.
See the recovery steps in
Operating and the account model in
Accounts and access.