Sign inSign up

nivekaacom/local-registry

By nivekaacom

•Updated 6 days ago

Lightweight local HTTP registry for uploading, publishing, and serving binaries and files

Image
Integration & delivery
Developer tools
Content management system
0

259

nivekaacom/local-registry repository overview

GoReleaser Logo


A self-hosted HTTP registry for goreleaser⁠ releases.

goreleaser builds your archives, binaries, checksums and its artifacts.json / metadata.json catalogue. local-registry stores them and serves them over HTTP, on your machine, your LAN or a private server, without GitHub or GitLab.

  • Publish safely: upload a release file by file, then publish it in one atomic step. The checksums and artifacts.json are checked first. A published version is immutable.
  • Download easily: every file gets a stable download URL. The served artifacts.json points to those URLs, not to the paths on the build machine.
  • Query releases: get the latest version, or any version, as JSON (artifacts.json, metadata.json, list of files with their URLs).
  • Secure by default: read-only unless an upload token is set, non-root container, upload quotas, audit log.

Source and full documentation: https://github.com/nivekalara237/local-registry⁠


⁠Supported platforms

HostHow
Linux x86-64 / ARM64Docker Engine (native linux/amd64, linux/arm64 images)
macOS Intel / Apple SiliconDocker Desktop, OrbStack, Colima
Windows x64 / ARM64Docker Desktop (WSL 2 backend)

Docker pulls the right architecture automatically. The same image is also published as ghcr.io/nivekalara237/local-registry.


⁠Quick start

⁠Linux and macOS
export LOCAL_REGISTRY_UPLOAD_TOKEN="$(openssl rand -hex 32)"
echo "$LOCAL_REGISTRY_UPLOAD_TOKEN"     # keep it: clients need it to publish

docker run -d --name local-registry --restart unless-stopped \
  -p 8077:8077 \
  -v local-registry-data:/data \
  -e LOCAL_REGISTRY_UPLOAD_TOKEN \
  nivekaacom/local-registry:latest

curl http://localhost:8077/healthz      # {"status":"ok"}
⁠Windows (PowerShell 7)
$env:LOCAL_REGISTRY_UPLOAD_TOKEN = [Convert]::ToHexString(
  [Security.Cryptography.RandomNumberGenerator]::GetBytes(32)).ToLower()
$env:LOCAL_REGISTRY_UPLOAD_TOKEN        # keep it: clients need it to publish

docker run -d --name local-registry --restart unless-stopped `
  -p 8077:8077 `
  -v local-registry-data:/data `
  -e LOCAL_REGISTRY_UPLOAD_TOKEN `
  nivekaacom/local-registry:latest

Invoke-RestMethod http://localhost:8077/healthz
⁠Docker Compose (any OS)

compose.yaml:

services:
  local-registry:
    image: nivekaacom/local-registry:latest
    restart: unless-stopped
    ports:
      - "8077:8077"
    volumes:
      - data:/data
      - ./config.yaml:/etc/local-registry/config.yaml:ro    # optional, see below
    environment:
      LOCAL_REGISTRY_CONFIG: /etc/local-registry/config.yaml # optional, see below
      LOCAL_REGISTRY_UPLOAD_TOKEN: ${LOCAL_REGISTRY_UPLOAD_TOKEN:?set it in .env}
volumes:
  data:

.env, next to compose.yaml, not committed:

LOCAL_REGISTRY_UPLOAD_TOKEN=<64 hex digits>
docker compose up -d

Without LOCAL_REGISTRY_UPLOAD_TOKEN, the registry is read-only: it serves what is already in /data, and the upload routes answer 403.


⁠Configuring the image

ItemValueNotes
Port8077Publish another host port with -p 9000:8077
Data volume/dataEvery release, the staging area and the audit log. Back it up.
Userregistry, uid/gid 10001Never root
Health checkGET /healthz every 30 sPlain HTTP on 127.0.0.1
CommandserveAlso config, version, help
Basealpine:3.24Includes ca-certificates, tzdata (set TZ)

Bind mount instead of a volume. The directory must be writable by uid 10001:

mkdir -p ./registry-data
sudo chown 10001:10001 ./registry-data          # Linux only; not needed on Docker Desktop
docker run … -v "$PWD/registry-data:/data" …

On Windows, use -v "${PWD}\registry-data:/data".


⁠Configuring the application

A setting is read from the first source that defines it:

  1. command-line flag, after the image name: … local-registry:latest serve --staging-ttl 48h
  2. environment variable LOCAL_REGISTRY_<FLAG>: -e LOCAL_REGISTRY_STAGING_TTL=48h
  3. YAML file given by LOCAL_REGISTRY_CONFIG
  4. default

The image already sets LOCAL_REGISTRY_ASSET_PATH=/data, LOCAL_REGISTRY_ADDRESS=0.0.0.0 and LOCAL_REGISTRY_PORT=8077. Environment variables win over the file, so asset-path, address and port in the YAML file have no effect. Change them with -e.

⁠Settings
Setting (YAML key)Environment variableDefaultDescription
—LOCAL_REGISTRY_UPLOAD_TOKEN—Upload token. Empty: read-only
public-urlLOCAL_REGISTRY_PUBLIC_URL—Address clients use, e.g. https://registry.example.com. Makes download URLs absolute; empty
gives relative URLs. Set it behind a proxy or on a server.
max-upload-mbLOCAL_REGISTRY_MAX_UPLOAD_MB200Largest uploaded file, MiB
max-version-mbLOCAL_REGISTRY_MAX_VERSION_MB2048Staged bytes per version, MiB (0: no limit)
max-version-filesLOCAL_REGISTRY_MAX_VERSION_FILES100Staged files per version (0: no limit)
max-staging-mbLOCAL_REGISTRY_MAX_STAGING_MB10240Staged bytes for all versions, MiB (0: no limit)
min-free-mbLOCAL_REGISTRY_MIN_FREE_MB1024Free disk space an upload must leave, MiB (0: no check)
staging-ttlLOCAL_REGISTRY_STAGING_TTL24hPurge unpublished uploads idle this long (0 or ≥ 1h)
audit-logLOCAL_REGISTRY_AUDIT_LOG/data/bin.local/audit.logAudit log of every upload and publish
tls-cert, tls-keyLOCAL_REGISTRY_TLS_CERT, LOCAL_REGISTRY_TLS_KEY—Serve HTTPS (see below)
disable-generate-tokenLOCAL_REGISTRY_DISABLE_GENERATE_TOKENtruefalse: generate a token at each start and print it in the
container logs
⁠With a YAML file

config.yaml:

public-url: https://registry.example.com
max-upload-mb: 500
max-version-mb: 4096
max-version-files: 200
staging-ttl: 48h
min-free-mb: 2048
docker run -d --name local-registry -p 8077:8077 \
  -v local-registry-data:/data \
  -v "$PWD/config.yaml:/etc/local-registry/config.yaml:ro" \
  -e LOCAL_REGISTRY_CONFIG=/etc/local-registry/config.yaml \
  -e LOCAL_REGISTRY_UPLOAD_TOKEN \
  nivekaacom/local-registry:latest
  • The file must be readable by uid 10001 (chmod 644 config.yaml).
  • Unknown keys are rejected, so a typo stops the container with an explicit error.
  • Pass the token with -e or the .env file rather than in the YAML file.
⁠Check the configuration
docker exec local-registry local-registry config

This prints every setting, its value and its source (flag, env …, config file, default). The token is never shown, only its token_id. To list every flag:

docker run --rm nivekaacom/local-registry serve --help
⁠HTTPS

The token travels in clear text over HTTP. On anything other than localhost, use HTTPS.

  • Recommended: a TLS reverse proxy (Traefik, Caddy, nginx) in front of port 8077, and LOCAL_REGISTRY_PUBLIC_URL=https://….

  • TLS in the container: mount the certificate and key, readable by uid 10001. Run with --no-healthcheck, because the built-in check uses plain HTTP.

    docker run -d --no-healthcheck -p 8443:8077 \
      -v local-registry-data:/data \
      -v "$PWD/certs:/certs:ro" \
      -e LOCAL_REGISTRY_TLS_CERT=/certs/fullchain.pem \
      -e LOCAL_REGISTRY_TLS_KEY=/certs/privkey.pem \
      -e LOCAL_REGISTRY_PUBLIC_URL=https://registry.example.com:8443 \
      -e LOCAL_REGISTRY_UPLOAD_TOKEN \
      nivekaacom/local-registry:latest
    

⁠Publishing a release

In your goreleaser project, after goreleaser release, use scripts/upload.sh. It is shipped in every archive of the GitHub releases⁠ and needs bash, curl and jq.

export LOCAL_REGISTRY_URL=http://localhost:8077
export LOCAL_REGISTRY_UPLOAD_TOKEN=<token>

scripts/upload.sh --latest github dist/artifacts.json

The script uploads every file listed in dist/artifacts.json, artifacts.json last, then publishes the version. github is a free name (<vcs>) grouping the versions of one product. --latest makes this version the one served by default.

⁠Reading releases

curl http://localhost:8077/github/artifacts                       # artifacts.json, latest version
curl http://localhost:8077/github/artifacts/v1.2.3                # artifacts.json, given version
curl http://localhost:8077/artifacts/metadatas/github             # metadata.json (?version=v1.2.3)
curl http://localhost:8077/artifacts/metadatas/github             # metadata.json (?version=v1.2.3)
curl -X POST http://localhost:8077/releases/github/latest         # files and their download URLs
curl -OJ http://localhost:8077/github/artifacts/v1.2.3/files/myapp_1.2.3_linux_amd64.tar.gz

⁠Logs and troubleshooting

docker logs -f local-registry     # one line per request; errors carry the exact cause
SymptomFix
403 on uploadNo token set in the container
401 on uploadClient and server tokens differ
Container exits with Error: …Configuration error: the message names the setting
permission denied under /dataBind-mounted directory not writable by uid 10001
Download URLs point to localhostSet LOCAL_REGISTRY_PUBLIC_URL

⁠Verifying the image

Images are signed keylessly with cosign (Sigstore, GitHub Actions) and carry an SBOM.

cosign verify nivekaacom/local-registry:<version> \
  --certificate-identity-regexp 'https://github.com/nivekalara237/local-registry/.github/workflows/release.yml@.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

⁠License and source

https://github.com/nivekalara237/local-registry⁠

Tag summary

Content type

Image

Digest

sha256:2634c38b0…

Size

8.4 MB

Last updated

6 days ago

docker pull nivekaacom/local-registry