Run the Sites as rootless Podman Quadlets, and build with podman
The fleet on emily is rootless Podman with Quadlet units under ~/.config/containers/systemd and state in ~/podman-data, all behind one shared Caddy unit. This brings the menus in line instead of leaving them the last Docker deployment. The build moves with them: podman reads a Containerfile as readily as a Dockerfile, so the name now says which toolchain is real. The app is one .container and one .network file. Two details carry it. UserNS=keep-id maps the image's own node account onto the user that owns the units, so data/ and site.env stay owned by tanshu: plain tar backups, no subuids. provision-site-env.sh writes site.env with the same mapping rather than --user, which under rootless Podman would have produced a subuid-owned file the app could not read. And the Site registers its own network with Caddy by writing a drop-in beside caddy.container, so Caddy never lists its upstreams and never changes when a Site is added. The unit keeps the Docker-era container name for the scripts and muscle memory that look it up, while the Caddyfile proxies to a dotless NetworkAlias, because a container name with dots in it is a liability in DNS. The unit keeps podman's Pull=missing, so a restart or a reboot never depends on the registry being reachable, while the play pulls explicitly at deploy time. That is also where a missing `podman login` for the private registry now fails, with the instruction, rather than as a unit that will not start. copy-site-data.sh moves a Site's data across, and is not a plain rsync: /var/lib/menu.* on monoco belongs to uid 1000 while the ssh user there is 1001, so the read needs sudo; monoco cannot ssh to emily, so the workstation relays the tar; and the extraction runs as tanshu on emily, which is what makes the files land owned by the user keep-id maps the app onto. It proves the copy by comparing a SHA-256 of the whole tree against the source and checking every file's owner. Not yet done: the copies themselves and the DNS move, which need a human.
This commit is contained in:
1 parent
c412181159
commit
e68f8ab00d
23 files changed
+876
-389
No files matched your search
-31
@@ -1,31 +0,0 @@
|
||||
# The app runs TypeScript directly on Node 24, so there is no build step and no
|
||||
# compiler in the image. Node 24 on Alpine is musl, which sharp ships prebuilt
|
||||
# binaries for.
|
||||
FROM node:24-alpine
|
||||
|
||||
ENV NODE_ENV=production
|
||||
WORKDIR /app
|
||||
|
||||
# Dependencies first, so editing source does not reinstall them.
|
||||
COPY --chown=node:node package.json package-lock.json ./
|
||||
RUN npm ci --omit=dev && npm cache clean --force
|
||||
|
||||
COPY --chown=node:node src ./src
|
||||
COPY --chown=node:node public ./public
|
||||
COPY --chown=node:node config ./config
|
||||
COPY --chown=node:node scripts ./scripts
|
||||
|
||||
# Config is mounted read-only; everything the app writes lives under MENU_ROOT.
|
||||
ENV MENU_ROOT=/data
|
||||
ENV MENU_SITE_CONFIG=/etc/menu-app/site.json
|
||||
ENV PORT=3000
|
||||
|
||||
RUN mkdir -p /data && chown node:node /data
|
||||
USER node
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
|
||||
CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||3000)+'/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
||||
|
||||
CMD ["node", "src/server.ts"]
|
||||
Reference in new issue
Block a user