orchard
mirrorEvery site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.
blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics
1name: orchard-analytics
2
3# No published ports. Traffic arrives through cloudflared to Caddy on the shared
4# network. State is a named volume, not a bind mount: the Docker CLI here talks
5# to Docker Desktop, which mounts a /home/dev path empty instead of erroring.
6
7services:
8 app:
9 build:
10 context: .
11 dockerfile: Dockerfile
12 container_name: orchard-analytics
13 restart: unless-stopped
14 # Longer than Docker's 10s default, because shutdown drains the log shipper
15 # queue and commits what the writer holds. Without it a slow drain means
16 # SIGKILL and a skipped db.Close().
17 stop_grace_period: 30s
18 volumes:
19 - data:/data
20 # A ceiling to fail against rather than a target, since this machine is also
21 # a workstation. Roomier than the static sites because of the 131MB GeoIP
22 # mmdb and the Typst report renders.
23 deploy:
24 resources:
25 limits:
26 cpus: "1.00"
27 memory: 512M
28 # The binary probes itself, since the scratch images here have no shell for a
29 # check to call, and `restart` only catches a process that has exited.
30 healthcheck:
31 test: ["CMD", "/app", "-healthcheck"]
32 interval: 30s
33 timeout: 5s
34 retries: 3
35 start_period: 10s
36 security_opt:
37 - no-new-privileges:true
38 # Otherwise container logs grow unbounded on a workstation.
39 logging:
40 driver: json-file
41 options:
42 max-size: "10m"
43 max-file: "3"
44 networks: [edge]
45
46volumes:
47 data:
48 name: orchard-analytics-data
49
50networks:
51 edge:
52 name: orchard-edge
53 external: true