Put the cloud Dockerfile at the repo root, so a platform pointed at the repo finds it
Railway, DigitalOcean and `docker build` use a root Dockerfile with no setting, and it is Coolify's and Dokploy's default path. The file moves; nothing is copied. - VOLUME /data is gone. Railway rejects the line, and a named volume is needed anyway (VOLUME alone gives each new container a fresh anonymous one). The platforms mount /data. - The baked PocketBase gate's comment named the old path. It now says the repo-root Dockerfile, and the gate's SHA-256 pin moves with it (2987055b... -> 2eceef03...). The hook's code is unchanged: the exact RUN passes `sha256sum -c`, and the old block still passes its old pin. - .dockerignore excludes everything: the Dockerfile copies nothing from the build context, so no platform sends the whole repository to the builder. - compose.yaml: the same image with its /data volume and memory limit; settings from .env. - deployment/cloud/README.md gains "Where it runs": Coolify, Dokploy, Railway, a server with Docker, Kubernetes, and why not DigitalOcean App Platform (no persistent volumes). DEPLOY.md and two comments follow the move.
This commit is contained in:
parent
9b4db40443
commit
7dc634275e
7 changed files with 60 additions and 15 deletions
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# The root Dockerfile copies nothing from the build context: at start the container clones the repo and
|
||||
# runs deployment/cloud/entry.sh from it. Excluding everything keeps a platform from sending the whole
|
||||
# repository to the builder. A Dockerfile that ever COPYs from the repo must re-include those paths here.
|
||||
*
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
# dx-maps cloud dev server: one shared Vite dev server with hot reload for a whole team.
|
||||
# This image is only a bootstrap: at start it clones the repo and runs deployment/cloud/entry.sh
|
||||
# FROM THE REPO, so the recipe lives in git and updates with it. Paste this file as-is into a
|
||||
# container host (Coolify "Dockerfile" build pack, Railway, or `docker build`).
|
||||
# FROM THE REPO, so the recipe lives in git and updates with it. It sits at the repo root so a
|
||||
# platform pointed at the repo finds it (Railway, DigitalOcean and `docker build` with no setting;
|
||||
# Coolify's and Dokploy's default Dockerfile path). It copies nothing from the build context
|
||||
# (.dockerignore excludes everything), so it can also be pasted as-is into a container host.
|
||||
# Mount a persistent volume at /data; the platforms are in deployment/cloud/README.md.
|
||||
FROM quay.io/oauth2-proxy/oauth2-proxy:v7.15.3 AS oauth2proxy
|
||||
FROM oven/bun:1-debian
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates curl unzip \
|
||||
|
|
@ -41,7 +44,7 @@ RUN curl -fsSL -o /tmp/pb.zip "https://github.com/pocketbase/pocketbase/releases
|
|||
# Changing it = edit these lines AND the SHA-256 below, then redeploy the image (an admin act).
|
||||
RUN mkdir -p /app/pb_hooks_gate && printf '%s\n' \
|
||||
'// Team sign-in gate: only members of OIDC_ALLOWED_GROUPS (the groups claim from the identity provider) may sign in here.' \
|
||||
'// Baked into the image by deployment/cloud/Dockerfile and pinned by SHA-256: never read from the pulled repo.' \
|
||||
'// Baked into the image by the repo-root Dockerfile and pinned by SHA-256: never read from the pulled repo.' \
|
||||
'onRecordAuthWithOAuth2Request((e) => {' \
|
||||
' const allow = ($os.getenv("OIDC_ALLOWED_GROUPS") || "").split(",").map((s) => s.trim()).filter((s) => s)' \
|
||||
' const raw = (e.oAuth2User && e.oAuth2User.rawUser) || {}' \
|
||||
|
|
@ -53,7 +56,7 @@ RUN mkdir -p /app/pb_hooks_gate && printf '%s\n' \
|
|||
' e.next()' \
|
||||
'}, "users")' \
|
||||
> /app/pb_hooks_gate/team-gate.pb.js \
|
||||
&& echo "2987055ba6067fc30d8162608da21c6c2b691fb6897a46d10b7075271a38f13d /app/pb_hooks_gate/team-gate.pb.js" | sha256sum -c -
|
||||
&& echo "2eceef03b18710b276b87ddab49e8aa9e65be5d54e27ef9ea6abf92c4cf122ee /app/pb_hooks_gate/team-gate.pb.js" | sha256sum -c -
|
||||
RUN printf '%s\n' \
|
||||
'#!/bin/bash' \
|
||||
'set -u; export GIT_TERMINAL_PROMPT=0' \
|
||||
|
|
@ -64,6 +67,5 @@ RUN printf '%s\n' \
|
|||
'cp /app/repo/deployment/cloud/entry.sh /tmp/entry.sh && exec bash /tmp/entry.sh' \
|
||||
> /boot.sh && chmod 755 /boot.sh
|
||||
ENV PORT=5173 BRANCH=main PULL_INTERVAL=15
|
||||
VOLUME /data
|
||||
EXPOSE 5173
|
||||
CMD ["/boot.sh"]
|
||||
22
compose.yaml
Normal file
22
compose.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# One dx-maps team container, the same image a platform builds from ./Dockerfile:
|
||||
# docker compose up -d --build
|
||||
# Settings come from a .env file next to this one (never committed): REPO_URL is required, GIT_TOKEN
|
||||
# for a private repo, and the OIDC_* settings for the sign-in; every setting is in deployment/cloud/README.md.
|
||||
services:
|
||||
dx-maps:
|
||||
build: .
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
environment:
|
||||
REPO_URL: ${REPO_URL:?set REPO_URL to the dx-maps git URL, in .env}
|
||||
BRANCH: ${BRANCH:-development}
|
||||
PRODUCTION_BRANCH: ${PRODUCTION_BRANCH:-main}
|
||||
ports:
|
||||
- "${PORT:-5173}:${PORT:-5173}"
|
||||
volumes:
|
||||
- dx-data:/data
|
||||
mem_limit: 2560m
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
dx-data:
|
||||
|
|
@ -11,10 +11,10 @@ and the hot-reload websocket. Only people in the allowed groups get in.
|
|||
|
||||
## Deploy
|
||||
|
||||
1. Build the image from [`Dockerfile`](./Dockerfile). It's a small bootstrap (Bun, git, and the
|
||||
[oauth2-proxy](https://github.com/oauth2-proxy/oauth2-proxy) binary); at start it clones the repo
|
||||
and runs [`entry.sh`](./entry.sh) from it, so the recipe updates with the repo. Paste it into any
|
||||
container host (Coolify's "Dockerfile" build pack, Railway, `docker build`).
|
||||
1. Build the image from the repo-root [`Dockerfile`](../../Dockerfile). It's a small bootstrap (Bun, git,
|
||||
and the [oauth2-proxy](https://github.com/oauth2-proxy/oauth2-proxy) binary); at start it clones the
|
||||
repo and runs [`entry.sh`](./entry.sh) from it, so the recipe updates with the repo. Point a
|
||||
container host at the repo, or paste the file into one: see [Where it runs](#where-it-runs).
|
||||
2. Set the environment below and expose the container's `PORT` over HTTPS.
|
||||
3. Give it about 2 GB of memory (Vite plus the dependency install).
|
||||
|
||||
|
|
@ -27,6 +27,22 @@ and the hot-reload websocket. Only people in the allowed groups get in.
|
|||
| `PORT` | no (`5173`) | the port the container serves on |
|
||||
| `HMR_CLIENT_PORT`, `HMR_PROTOCOL` | behind HTTPS | `443` and `wss`, so browsers reach the hot-reload websocket |
|
||||
|
||||
## Where it runs
|
||||
|
||||
The container needs a Dockerfile build, a persistent volume at `/data` and about 2.5 GB of memory.
|
||||
|
||||
| Platform | Build | `/data` |
|
||||
|---|---|---|
|
||||
| Coolify | the "Dockerfile" build pack (default location `/Dockerfile`) | Persistent Storage, mount path `/data` |
|
||||
| Dokploy | Build Type "Dockerfile" (path `Dockerfile`) | Advanced, Volumes: mount path `/data` |
|
||||
| Railway | finds the root `Dockerfile` by itself; set `PORT=5173` | add a volume at `/data`. Railway deploys from GitHub, a local directory or an image |
|
||||
| A server with Docker | `docker compose up -d --build`, with the root [`compose.yaml`](../../compose.yaml) | the compose file's `dx-data` volume |
|
||||
| Kubernetes | build the image in CI (BuildKit, Buildah) and push it to a registry | a PersistentVolumeClaim at `/data` |
|
||||
| DigitalOcean App Platform | not suitable | it has no persistent volumes, so PocketBase's data would be lost on every deploy |
|
||||
|
||||
There is no Railpack file. Railpack is Railway's default builder (Coolify and Dokploy offer it too), but it
|
||||
can't reuse this Dockerfile; the container is defined here, once.
|
||||
|
||||
## Sign-in (optional)
|
||||
|
||||
Set all of these to turn the gate on. Register a **confidential** client at your OIDC provider
|
||||
|
|
|
|||
|
|
@ -40,13 +40,14 @@ or for an agent that lives on a server. Nobody edits files on it: changes arrive
|
|||
`development`, the branch the team lands on, and publishes `main` (production) at `/cdn` (way 3).
|
||||
|
||||
```bash
|
||||
docker build -t dx-maps-dev deployment/cloud
|
||||
docker build -t dx-maps-dev .
|
||||
docker run -d -p 5173:5173 -v dx-maps-data:/data \
|
||||
-e REPO_URL=https://<host>/<owner>/<repo>.git -e BRANCH=development -e PRODUCTION_BRANCH=main dx-maps-dev
|
||||
```
|
||||
|
||||
Or paste `deployment/cloud/Dockerfile` into a container host (Coolify's "Dockerfile" build pack) and give
|
||||
it a domain.
|
||||
Or `docker compose up -d --build` with the root `compose.yaml`, or point a container host at the repo: it
|
||||
finds the root `Dockerfile` (the platforms are in [deployment/cloud/README.md](../deployment/cloud/README.md#where-it-runs)).
|
||||
Give it a domain.
|
||||
|
||||
- **Give it a persistent `/data` volume.** It holds the team PocketBase (dev and prod) and the last
|
||||
published build. Without one, every restart or redeploy starts empty.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import { inflateRawSync } from 'node:zlib'
|
|||
import { resolve } from 'node:path'
|
||||
|
||||
// PocketBase, the backend for plugins that need one: the SAME pinned version as the cloud container
|
||||
// (deployment/cloud/Dockerfile), SHA-256 from the release's checksums.txt. Downloaded on the first
|
||||
// (the repo-root Dockerfile), SHA-256 from the release's checksums.txt. Downloaded on the first
|
||||
// `bun run dev` into backend/bin (gitignored); its data lives in .pb/dev (gitignored).
|
||||
const PB_VERSION = '0.40.4'
|
||||
const PB_SHA256 = {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* WHY: the cloud container pulls this repo every few seconds. A PocketBase hook is server-side JS; one pushed
|
||||
* by any plugin writer would run on the team backend within seconds, which is the rogue-writer hole the
|
||||
* roundtable test closed. The container never points PocketBase at the repo (--hooksDir is outside the
|
||||
* pulled tree; the one team sign-in hook is baked into the image and pinned, deployment/cloud/Dockerfile).
|
||||
* pulled tree; the one team sign-in hook is baked into the image and pinned, in the repo-root Dockerfile).
|
||||
* This gate keeps anyone from adding a hooks folder that LOOKS like it would run, or that a later recipe
|
||||
* change could start running.
|
||||
*
|
||||
|
|
@ -26,7 +26,7 @@ const tracked = execFileSync('git', ['ls-files', '-z'], { cwd: ROOT }).toString(
|
|||
const bad = offending(tracked)
|
||||
if (bad.length) {
|
||||
console.error(`✗ PocketBase hooks in the repo (server-side JS must never ride the live pull):\n${bad.map((p) => ' ' + p).join('\n')}`)
|
||||
console.error(' The team sign-in hook is baked into the image (deployment/cloud/Dockerfile); anything else needs a design review.')
|
||||
console.error(' The team sign-in hook is baked into the image (the repo-root Dockerfile); anything else needs a design review.')
|
||||
process.exit(1)
|
||||
}
|
||||
console.log(`✓ no PocketBase hooks among ${tracked.length} tracked files`)
|
||||
|
|
|
|||
Loading…
Reference in a new issue