Repository navigation
Expand file tree
/
Copy pathcompose.yml
More file actions
144 lines (143 loc) · 7.93 KB
/
Copy pathcompose.yml
File metadata and controls
144 lines (143 loc) · 7.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# openplate, self-hosted: one stateless container and nothing else.
#
# mkdir -p ~/openplate && cd ~/openplate
# curl -O https://raw.githubusercontent.com/LowCarbCheck/openplate/main/docker/compose.yml
# docker compose -f compose.yml up -d
#
# Keep this file in a folder that lasts, like ~/openplate above, and run every
# command from there. Compose treats the compose file's OWN directory as the
# project directory, so a `.env` beside this file is the one it reads, not one
# somewhere further up. A folder under /tmp can be emptied by a reboot.
#
# Only http://localhost:3000 on this machine counts as a secure page. Opened
# from another device at http://<this machine's address>:3000, the diary and
# plate photos work, but installing the app, offline use and the one-click
# OpenRouter connect do not. See the HTTPS section of apps/app/docs/self-hosting.md.
#
# There is no database and no secret to configure. The server stores nothing:
# your diary lives in your browser's own IndexedDB on the device you use it
# from (see apps/app/.adr/0006-the-app-server-holds-no-accounts.md). Back up with the
# in-app JSON export, not with a database dump.
#
# If you also want the optional core server (accounts and diary sync), use
# docker/topologies/compose.core.yml instead. THAT one needs a database,
# because the core server keeps accounts and ciphertext. The other shapes
# (self-hosted AI, or everything at once) sit beside it; docker/topologies/
# README.md is the one-page map.
# Fixes the project name. Without it Compose names the stack after the
# directory the file sits in -- "docker" for anyone running from a checkout --
# and every container and volume inherits that name.
name: openplate
services:
# Pulls the published multi-arch image from GHCR by default -- no source
# checkout required for a self-host. To build from source instead (e.g.
# developing against this repo), comment out `image:` below and uncomment
# `build:`, then run:
# docker compose --project-directory . -f docker/compose.yml build
# docker compose --project-directory . -f docker/compose.yml up -d
# Run those two from the repo root. The build context below is written
# relative to THIS file, so it points one level up and into the app's folder
# of the checkout, apps/app, and `--project-directory .` is what makes
# Compose read the repo root's `.env` instead of the one it would look for
# in `docker/`.
app:
image: ghcr.io/lowcarbcheck/openplate:latest
# build:
# context: ../apps/app
# dockerfile: Dockerfile.pnpm
restart: unless-stopped
ports:
# Published on EVERY network interface of this machine, so anyone on your
# network can open http://<this machine's address>:3000. Behind a reverse
# proxy on this machine, write '127.0.0.1:3000:3000' instead: then only
# the proxy can reach the app, and the plain HTTP port is gone.
- '3000:3000'
# To serve legal pages, uncomment this and set CONTENT_DIR in `.env`:
# CONTENT_DIR=/srv/openplate/content
# volumes:
# - ./content:/srv/openplate/content:ro
environment:
NODE_ENV: production
PORT: 3000
# Public URL this instance is reachable at. Set APP_URL in .env if you
# put a reverse proxy in front (see apps/app/docs/self-hosting.md, HTTPS).
APP_URL: ${APP_URL:-http://localhost:3000}
# ON by default. Queries the public LowCarbCheck food database for
# curated nutrition data (food NAMES only, never photos; fails open on
# outages). An EMPTY string disables it entirely so no food names ever
# leave your machine.
FOOD_DB_API_URL: ${FOOD_DB_API_URL-https://lowcarbcheck.org}
# Optional free key for that database. Empty is the shared anonymous
# allowance; set one if more than one person scans on this instance.
FOOD_DB_API_KEY: ${FOOD_DB_API_KEY:-}
# "true" passes foods people save from an AI answer on to LowCarbCheck as
# proposals. Needs FOOD_DB_API_KEY. Empty means off.
FOOD_DB_BACKFILL: ${FOOD_DB_BACKFILL:-}
# The most LowCarbCheck calls this server makes in one UTC day. Empty means
# the default, 3200.
FOOD_DB_DAILY_CALL_LIMIT: ${FOOD_DB_DAILY_CALL_LIMIT:-}
# Number of reverse proxies in front of this container. Behind one proxy
# (Caddy, nginx, Traefik) keep 1: React Router's CSRF check compares the
# browser Origin against the host it thinks it is serving, and without
# the proxy's X-Forwarded-* headers form posts fail. With NO proxy, set
# TRUST_PROXY=0 in .env: pages work either way, but 1 lets any visitor
# fake their address in X-Forwarded-For and dodge the per-address limit
# on food lookups. 2 = Cloudflare in front of one proxy.
TRUST_PROXY: ${TRUST_PROXY:-1}
# The language a first-time visitor sees: en, de, fr, it, es or tr.
# Empty means en. Anyone can still switch in Settings.
DEFAULT_UI_LANGUAGE: ${DEFAULT_UI_LANGUAGE:-}
# "off" disables the six-hourly request to openplate.de for the newest version
# and the project's daily count of asks. Empty means on.
UPDATE_CHECK: ${UPDATE_CHECK:-}
# Closes this instance: an https:// address where its people went.
# Every page then names it. Empty means open as usual.
MOVED_TO_URL: ${MOVED_TO_URL:-}
# Extra origins the browser may call, space separated, for example a
# remote AI endpoint of your own. Empty adds nothing.
CSP_CONNECT_EXTRA: ${CSP_CONNECT_EXTRA:-}
# debug, info, warn or error.
LOG_LEVEL: ${LOG_LEVEL:-info}
# The address of openplate-core, one a BROWSER can reach.
# Empty means no sync. docker/topologies/compose.core.yml runs one.
CORE_URL: ${CORE_URL:-}
# The old name of CORE_URL, read for one more release. Set CORE_URL.
SYNC_SERVER_URL: ${SYNC_SERVER_URL:-}
# open (the default) or managed, which needs CORE_URL. See
# apps/app/docs/configuration.md, Managed instances.
INSTANCE_MODE: ${INSTANCE_MODE:-open}
# An OpenAI-compatible vision endpoint every browser here may use with
# one tap, at an address a BROWSER can reach. The key is PUBLIC: every
# browser that loads the app can read it. Empty means each person brings
# their own key. The model defaults to openplate-plate-1.
DEFAULT_INFERENCE_BASE_URL: ${DEFAULT_INFERENCE_BASE_URL:-}
DEFAULT_INFERENCE_API_KEY: ${DEFAULT_INFERENCE_API_KEY:-}
DEFAULT_INFERENCE_MODEL: ${DEFAULT_INFERENCE_MODEL:-}
# Which published reference values the Nutrients screen quotes: dge (the
# default), efsa or us.
NUTRIENT_REFERENCE_BASIS: ${NUTRIENT_REFERENCE_BASIS:-dge}
# Matomo analytics, off unless the first two are set together. The level
# is pageviews, product (the default when empty) or research, and only
# with the pair: a level on its own stops the boot.
MATOMO_URL: ${MATOMO_URL:-}
MATOMO_SITE_ID: ${MATOMO_SITE_ID:-}
MATOMO_EVENT_LEVEL: ${MATOMO_EVENT_LEVEL:-}
# A newsletter form on the landing page, off unless both are set.
NEWSLETTER_SUBSCRIBE_URL: ${NEWSLETTER_SUBSCRIBE_URL:-}
NEWSLETTER_TURNSTILE_SITE_KEY: ${NEWSLETTER_TURNSTILE_SITE_KEY:-}
# The folder of legal pages, mounted read-only. Set it to the container
# path of the volume line above. Empty means no legal pages.
CONTENT_DIR: ${CONTENT_DIR:-}
# The address the server binds to inside the container. Leave it empty:
# the published port reaches only a server on every interface.
HOST: ${HOST:-}
healthcheck:
# A shell line with no quotes and no brackets, run by the image's busybox
# wget. Docker Compose, podman-compose and Quadlet all pass it through
# the same way; the old `node -e "fetch(...)"` form reached podman-compose
# 1.0.6 as a broken shell line and stayed unhealthy forever.
test: ['CMD-SHELL', 'wget -q -O /dev/null http://127.0.0.1:3000/healthcheck']
interval: 30s
timeout: 5s
start_period: 30s
retries: 3