You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make Collectify trivially installable for a non-developer self-hoster: pull a tagged image from a registry, drop a small docker-compose.yml, set a few env vars, docker compose up -d. No clone, no toolchain, no docker build.
Do we need to split the build image and the consumer image?
No. The current Dockerfile is already multi-stage:
client-build (node:22-alpine) → builds the React bundle.
server-build (mcr.microsoft.com/dotnet/sdk:10.0) → restores + publishes the API; copies React dist/ into wwwroot/.
runtime (mcr.microsoft.com/dotnet/aspnet:10.0) → final image with only the published bits + non-root user.
Only the runtime stage ships. The SDKs are intermediate stages and get discarded. This is the standard "builder vs consumer" split, just inside one Dockerfile. Splitting into two top-level images would double the maintenance and buy nothing. Keep the single multi-stage Dockerfile.
What we do need is to publish the runtime image so self-hosters never run docker build themselves, plus a few quality-of-life bits below.
What's missing for a real release
1. Publish to a registry (Gitea-first, registry-agnostic workflow)
The maintainer's primary target is a self-hosted Gitea instance, but the release workflow should be parameterised by env/secrets so swapping to GHCR, Docker Hub, or any other OCI registry is a config change, not a code change.
docker login $REGISTRY -u "$REGISTRY_USER" -p "$REGISTRY_TOKEN", then build/push to $REGISTRY/$IMAGE_NAME:<tag>.
docker-compose.yml currently says build: ., which forces clone-and-build. Switch the consumer compose to image: ${REGISTRY:-ghcr.io}/${IMAGE_NAME:-mforce/collectify}:${TAG:-latest} with env-var defaults so the same file works against any registry.
Keep build: . available in a docker-compose.dev.yml (or as an override) for contributors.
2. Multi-arch builds
linux/amd64 (NAS, mini-PC, x86 home server).
linux/arm64 (Raspberry Pi 4/5, Apple Silicon, Ampere VMs).
Build via docker buildx + QEMU. Same workflow, two platforms in one manifest.
Tie release tag → image tag → Collectify.Api.dll assembly version. Either bump <Version> in Collectify.Api.csproj per release, or pass --version-suffix / -p:Version= at publish time from the workflow.
Surface the version somewhere the UI can show it (extend /api/auth/me, or add a tiny /api/version). Helps bug reports.
5. Healthcheck
There's no /api/health today. Add a cheap endpoint that returns { status: "ok", version } without touching the DB (avoid making the healthcheck a write/migration-stall trap). Then wire it in the Dockerfile:
Compose / Watchtower / k8s liveness probes all key off this.
6. Consumer-facing compose example + README rewrite
README "Quick start" should show the pull-from-registry flavour, not clone-and-build.
Document the /data volume (DB + cover BLOBs), the optional .env for provider keys (Collectify__Metadata__Tmdb__ApiKey etc), and how to override the registry/image/tag for users on different infra.
Sample .env already exists; cross-link it.
7. Auto-backup before EF Core migrations (nice-to-have)
Migrations are applied on startup. If a self-hoster pulls a new image and a migration breaks them, they need their previous collectify.db.
On boot, if assembly version > recorded version in DB, copy collectify.db → /data/backups/collectify-<old-version>-<utc>.dbbeforeDatabase.MigrateAsync().
Rotate to last N backups (default 10) so the volume doesn't grow unbounded.
Document the recovery path: stop container, swap DB file, start.
8. CHANGELOG.md
Standard "Keep a Changelog" format. Self-hosters who pull a new tag want to know what changed without diffing.
9. Optional polish
Pin base-image digests for reproducibility (mcr.microsoft.com/dotnet/aspnet:10.0@sha256:…). Renovate/Dependabot can keep them fresh.
Try mcr.microsoft.com/dotnet/aspnet:10.0-alpine for a smaller image; benchmark before/after.
Add OCI labels (org.opencontainers.image.source/licenses/description/version) so the registry's package page looks right.
Run trivy or grype on the published image in CI; fail on HIGH/CRITICAL.
Suggested phasing (each independent enough to be its own PR)
Health endpoint + Dockerfile HEALTHCHECK — no infra dependencies, smallest scope.
Release workflow + multi-arch publish (registry-agnostic via env/secrets; default to maintainer's Gitea).
Goal
Make Collectify trivially installable for a non-developer self-hoster: pull a tagged image from a registry, drop a small
docker-compose.yml, set a few env vars,docker compose up -d. No clone, no toolchain, nodocker build.Do we need to split the build image and the consumer image?
No. The current
Dockerfileis already multi-stage:client-build(node:22-alpine) → builds the React bundle.server-build(mcr.microsoft.com/dotnet/sdk:10.0) → restores + publishes the API; copies Reactdist/intowwwroot/.runtime(mcr.microsoft.com/dotnet/aspnet:10.0) → final image with only the published bits + non-root user.Only the
runtimestage ships. The SDKs are intermediate stages and get discarded. This is the standard "builder vs consumer" split, just inside one Dockerfile. Splitting into two top-level images would double the maintenance and buy nothing. Keep the single multi-stage Dockerfile.What we do need is to publish the runtime image so self-hosters never run
docker buildthemselves, plus a few quality-of-life bits below.What's missing for a real release
1. Publish to a registry (Gitea-first, registry-agnostic workflow)
The maintainer's primary target is a self-hosted Gitea instance, but the release workflow should be parameterised by env/secrets so swapping to GHCR, Docker Hub, or any other OCI registry is a config change, not a code change.
REGISTRY(e.g.git.example.com),IMAGE_NAME(e.g.mforce/collectify),REGISTRY_USER,REGISTRY_TOKEN.docker login $REGISTRY -u "$REGISTRY_USER" -p "$REGISTRY_TOKEN", then build/push to$REGISTRY/$IMAGE_NAME:<tag>.docker-compose.ymlcurrently saysbuild: ., which forces clone-and-build. Switch the consumer compose toimage: ${REGISTRY:-ghcr.io}/${IMAGE_NAME:-mforce/collectify}:${TAG:-latest}with env-var defaults so the same file works against any registry.build: .available in adocker-compose.dev.yml(or as an override) for contributors.2. Multi-arch builds
linux/amd64(NAS, mini-PC, x86 home server).linux/arm64(Raspberry Pi 4/5, Apple Silicon, Ampere VMs).docker buildx+ QEMU. Same workflow, two platforms in one manifest.3. Release pipeline (
.github/workflows/release.yml)pushof tag matchingv*.*.*.dotnet test+npm test -- --run.:vX.Y.Z,:vX.Y,:vX,:latest.--build-arg/LABEL(commit SHA, build date, version).cosignprovenance attestation.4. Versioning
Collectify.Api.dllassembly version. Either bump<Version>inCollectify.Api.csprojper release, or pass--version-suffix/-p:Version=at publish time from the workflow./api/auth/me, or add a tiny/api/version). Helps bug reports.5. Healthcheck
There's no
/api/healthtoday. Add a cheap endpoint that returns{ status: "ok", version }without touching the DB (avoid making the healthcheck a write/migration-stall trap). Then wire it in the Dockerfile:Compose / Watchtower / k8s liveness probes all key off this.
6. Consumer-facing compose example + README rewrite
/datavolume (DB + cover BLOBs), the optional.envfor provider keys (Collectify__Metadata__Tmdb__ApiKeyetc), and how to override the registry/image/tag for users on different infra..envalready exists; cross-link it.7. Auto-backup before EF Core migrations (nice-to-have)
Migrations are applied on startup. If a self-hoster pulls a new image and a migration breaks them, they need their previous
collectify.db.collectify.db→/data/backups/collectify-<old-version>-<utc>.dbbeforeDatabase.MigrateAsync().8. CHANGELOG.md
Standard "Keep a Changelog" format. Self-hosters who pull a new tag want to know what changed without diffing.
9. Optional polish
mcr.microsoft.com/dotnet/aspnet:10.0@sha256:…). Renovate/Dependabot can keep them fresh.mcr.microsoft.com/dotnet/aspnet:10.0-alpinefor a smaller image; benchmark before/after.org.opencontainers.image.source/licenses/description/version) so the registry's package page looks right.Suggested phasing (each independent enough to be its own PR)
HEALTHCHECK— no infra dependencies, smallest scope.Out of scope