Saturday, 8 November 2025

Updating the Package Server – Auth, Probes, and a Bit of Cleanup

It’s been a while since I last wrote about the package server project. In that post, I’d just finished stabilizing the upload flow and getting the dynamic mirror logic working. Since then, I’ve pushed a fair number of updates — tightening authentication, cleaning up CI, and finally adding the small operational touches that make it more comfortable to run day to day.

Cleaning up after August

After I wrapped up the mirror work in August, I spent some time chasing edge cases and wiring up the CI. That work landed in a couple of late-September releases — mostly housekeeping and the first experiments toward a proper auth stack.
By November, the branch had grown into a decent fall cleanup: new auth modes, proper health endpoints, saner logs, and the usual round of dependency bumps and formatting fixes.

Authentication done right (or at least, done)

I finally formalized authentication. You can now run the server with no auth, basic file-based auth, an API backend that returns a JWT, or full OIDC integration. That gives me a smooth path from local testing through production, all using the same configuration block.

auth:
  mode: basic
  basic_htpasswd_file: ./.htpasswd
  basic_api_backends:
    - https://{rest server auth}

I spent more time than I’d like to admit making sure those combinations actually worked — the “none/basic/OIDC” wiring was surprisingly fiddly. The payoff, though, is that it now supports the same JWT-based flow I use elsewhere for CI jobs and other internal services.

Health endpoints and quieter logs

While working through container orchestration setups, I finally added proper /health and /ready endpoints. They exist mainly so Kubernetes probes don’t clutter the logs with noise.
At the same time, I reworked logging to use a standard Apache-style format and correctly handle X-Forwarded-For, so it’s finally possible to see who’s actually talking to the server through a proxy chain.

Mirrors that behave like mirrors

The dynamic pull-through mirrors I introduced last time are now more flexible. You can chain mirrors — useful when running a local cache in front of a site-wide one — and control the upstream via environment variables.
Set REGISTRY_UPSTREAM_MIRROR and flip enableProxy to true in the config, and the server will behave as a caching proxy without additional plumbing.

It’s a small feature, but it makes it much easier to run the server as part of a layered mirror setup.

CI and releases

Most of the September commits were about getting CI to publish images reliably. That’s all cleaned up now — image pushes happen automatically, and local builds match what I push from CI.
If you just want to run the latest build, you can grab it directly from Docker Hub:

docker pull jlcox1970/package-server:<tag>

I also standardized the module layout so it’s easier to build locally without wrestling with paths or dependencies.

Upgrading in place

If you’re already running an older instance, upgrades should be painless:

  • Keep your existing mirror and storage settings.
  • Switch your auth mode from “none” to “basic” (and point it to an .htpasswd file).
  • Add basic_api_backends if you want to authenticate against an external service.
  • Redirect liveness and readiness checks to the new endpoints.
  • Optionally enable REGISTRY_UPSTREAM_MIRROR to save bandwidth on cold pulls.

The default behavior hasn’t changed — just more options where they make sense.

Looking ahead

Next up is tightening the OIDC path and publishing a short “cookbook” of operational examples. I’ve had a few requests for topologies that mix public mirrors, internal caches, and private registries, so I’ll document what I’m using in production once it settles down.

As always, the code’s on GitLab at
https://gitlab.com/jlcox70/repository-server

and the Docker image builds are automatically pushed to jlcox1970/package-server on Docker Hub.

No comments:

Post a Comment