Grom is a lightweight, self-hosted OCI registry for individuals and small teams. It combines CNCF Distribution with a secure control plane and a simple web interface for access, images, and recovery.
- Project-based Docker image push and pull.
- Reader, Writer, and Admin roles.
- Service accounts with reveal-once, revocable access keys.
- Web management for users, projects, repositories, and policies.
- Repository inventory, deletion previews, and manual retention runs.
- Built-in backup and loopback-only recovery.
Projects are the security boundary. Readers can pull, Writers can push, and project Admins manage project settings. Installation administrators create projects, users, service accounts, and recovery points.
Human passwords never work as registry credentials. New users set their own password through a single-use registration link. Service-account keys are shown once and can be revoked at any time.
Writers can create a repository on first push. The UI shows repositories, tags, and manifests without giving registry clients delete permission. Archiving stops new pushes while keeping pulls available.
Deletion and retention are previewed first and require explicit confirmation. Grom protects OCI subject/referrer relationships and never deletes OCI content when only a logical repository record is removed.
cp .env.example .env
docker compose --env-file .env -f deploy/compose/docker-compose.yml up --buildOpen http://localhost:8080 and sign in with the bootstrap credentials in
.env. Change the example password before exposing Grom outside localhost.
Create a project, service account, membership, and access key in the web UI. Then use the project slug as the first path segment:
docker login localhost:8080
docker tag my-image:latest localhost:8080/my-project/my-image:latest
docker push localhost:8080/my-project/my-image:latest| Profile | Use |
|---|---|
development |
Local HTTP on loopback only. |
permissive |
Trusted private networks; HTTP needs explicit opt-in and shows a warning. |
strict |
Default for production; requires HTTPS and secure cookies. |
For a reverse proxy, set GROM_PUBLIC_URL, keep GROM_SECURE_COOKIES=true,
and list only immediate proxy networks in GROM_TRUSTED_PROXIES.
Installation administrators create and download recovery points from Backup & recovery. Grom pauses writes briefly, verifies the bundle, and resumes normal work. Store downloaded bundles encrypted and off-host.
For volume loss, start the same image in the loopback-only recovery profile
(port 8081 by default). Recovery accepts only verified bundles and empty
target volumes. Restored web sessions and reset links are invalidated on the
first normal boot.
Stable tags publish images to ghcr.io/jfxdev/grom-registry. Use the immutable
digest from the GitHub Release, not a mutable tag. Releases include an SBOM,
vulnerability report, and checksums.
Before upgrading, create and download a verified recovery point. Set
GROM_IMAGE to the new digest, start with docker compose pull and
docker compose up -d --no-build, then check /readyz and /api/docs. Do not
downgrade a database by changing only the image; restore a compatible backup.
# Start the Go backend and Vue frontend
make dev
# Start local development with PostgreSQL
make dev-postgres
# Build and run the regular quality gate
make build
make test| Command | Coverage |
|---|---|
make test-registry-e2e |
Public Docker authorization, push/pull, policies, and inventory. |
make test-admin-e2e |
Browser-based administrator and first-push flows. |
make test-boot-acceptance |
Boot, migrations, readiness, and API docs. |
make test-backup-restore-e2e |
SQLite backup and recovery. |
make test-backup-restore-postgres-e2e |
PostgreSQL backup and recovery. |
make test-release-upgrade-e2e |
Upgrade from a tagged release. |
make test-production-image-smoke |
Clean production image and public runtime. |
Docker acceptance tests use isolated resources only and require Docker Engine and Docker Compose.
The Go backend owns four small areas: Identity (users and credentials), Projects (roles), Registry (repositories and tokens), and Audit. CNCF Distribution remains private and stores OCI payloads; Grom is the only public entry point.
The Vue 3 / TypeScript frontend uses the API contract at
backend/api/openapi.yaml. Run make generate after changing that contract;
never edit generated code directly.
The supported path is one active installation with local registry storage and Docker image push/pull. High availability, S3 storage, replication, enterprise identity, generic OCI/ORAS support, automatic retention purging, and full audit browsing are outside this MVP.
Detailed design and operational records remain in docs/:
product rules,
architecture,
backup and recovery,
release operations, and the
contribution guide.