{"id":34182792,"url":"https://github.com/vleurgat/regstat","last_synced_at":"2026-03-12T15:39:44.481Z","repository":{"id":144422051,"uuid":"156126684","full_name":"vleurgat/regstat","owner":"vleurgat","description":"RegStat - persisting Docker registry notifications.","archived":false,"fork":false,"pushed_at":"2018-12-20T21:58:43.000Z","size":53,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-12-18T13:12:39.692Z","etag":null,"topics":["blob","docker","docker-registry","golang","images","manifest","notification-server","notifications","persistent","postgresql","registry","registry-data","regstat"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vleurgat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-11-04T21:26:49.000Z","updated_at":"2018-12-20T21:58:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"4b84c9a9-c8dc-4fbd-829e-a221dcbb96ae","html_url":"https://github.com/vleurgat/regstat","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/vleurgat/regstat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vleurgat%2Fregstat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vleurgat%2Fregstat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vleurgat%2Fregstat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vleurgat%2Fregstat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vleurgat","download_url":"https://codeload.github.com/vleurgat/regstat/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vleurgat%2Fregstat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30431029,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-12T14:34:45.044Z","status":"ssl_error","status_checked_at":"2026-03-12T14:09:33.793Z","response_time":114,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blob","docker","docker-registry","golang","images","manifest","notification-server","notifications","persistent","postgresql","registry","registry-data","regstat"],"created_at":"2025-12-15T14:15:17.968Z","updated_at":"2026-03-12T15:39:44.456Z","avatar_url":"https://github.com/vleurgat.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RegStat - persisting Docker registry notifications\n\nThe v2 Docker registry (also known as [Docker distribution](https://github.com/docker/distribution)) can be\nconfigured to notify via a webhook whenever any events (push, pull, delete) occur.\n\nThis simple application is designed to act as a server for those webhooks, parsing the webhook JSON body\nand then persisting details of a registry's activity into a Postgres database.\n\nThe contents of the database can then be queried to determine interesting facts about the objects in the\nregistry, facts that are not easy to astertain directly from the registry itself.\n\nFor instance ...\n\n* list of images in the registry\n* total number of blobs in the registry\n* images that are more than X days old\n* images that haven't been pulled for Y months\n* orphaned blobs\n* manifests that reference missing blobs\n* images with the most tags\n\n## Database schema\n\nRegStat expects to be provided with a connection to a Postgres database. Any modern Postgres version should\ndo. It's been tested with 9.5 and 10.3.\n\nOn start up RegStat will attempt to create a `regstat` schema and the following tables in that schema, if\nthey don't already exist ...\n\ntable | columns | description\n----- | ------- | -----------\nblobs | digest, pushed, pulled | list of blobs in the registry \nmanifests | digest, pushed, pulled | list of manifests in the registry\nmanifest_blob | manifest_digest, blob_digest | join table, linking manifests to their blobs\ntags | name, registry, repository, tag, manifest_digest, pushed, pulled | list of tags in the registry and the manifests that they represent; name is a concatenation of registry, repository and tag\ndeleted_blobs | digest, pushed, pulled, deleted | list of deleted blobs in the registry \ndeleted_manifests | digest, pushed, pulled, deleted | list of deleted manifests in the registry\ndeleted_manifest_blob | manifest_digest, blob_digest, deleted | join table, linking deleted manifests to their deleted blobs\ndeleted_tags | name, registry, repository, tag, manifest_digest, pushed, pulled, deleted | list of deleted tags in the registry and the deleted manifests that they represent\n\nThe `blobs`, `manifests` and `tags` tables, and the `deleted_` equivalents, all contain `pushed` and `pulled` timestamp fields, which contain the time\nof the most recent push or pull event that affected that object.\n\nThe four `deleted_` tables are the same as the main tables, except for the addition of an extra `deleted` timestamp column and the\ndropping of some constraints. These, fairly obviously, get populated as registry objects are deleted. They are\nintended to act as an audit trail for deletion events.\n\n## Running RegStat\n\n### Docker\n\nQuick and easy means to get RegStat going.\n\nThis starts a Postges database in a container and RegStat in another container. The RegStat server is listening on port 3333. Postgres is accessible as user \"postgres\" with no password on port 5432.\n\nObviously, for any semi-serious use, you'll want to use a Postgres database backed by reliable storage and likely add more options to the Regstat command line.\n\n````\n$ docker run -d --name=regstat-postgres -e POSTGRES_PASSWORD='' -p 5432:5432 postgres:10\n$ docker run -d --name=regstat --net=host vleurgat/regstat:latest\n````\n\n(Note that the `--net=host` option is important. It allows the `regstat` container to connect to the `regstat-postgres` one as `localhost:5432`. There are other, better, ways to link these containers, but this works well enough for a simple example.)\n\nYou can supply extra RegStat args on the end of the second `docker run` command ...\n\n````\n$ docker run -d --name=regstat --net=host vleurgat/regstat:latest -port 4444\n$ docker logs regstat\n1970/01/01 22:34:45 Server now listening on :4444\n````\n\nAlso, for example, adding config files via a volume and additional arguments ...\n\n````\n$ docker run -d --name=regstat --net=host \\\n  -v $HOME/cfgfiles:/cfgfiles \\\n  vleurgat/regstat:latest \\\n  -docker-config /cfgfiles/config.json \\\n  -equiv-registries /cfgfiles/equiv-registries.json\n````\n\n### Command line\n\n````\n$ regstat -h\nUsage of regstat:\n  -docker-config string\n    \tthe path to the Docker registry config.json file, used to obtain login credentials\n  -equiv-registries string\n    \tthe path to the equiv-registries.json file, used to combine equivalent registries\n  -pg-conn-str string\n    \tthe Postgres connect string, e.g. \"host=host port=1234 user=user password=pw ...\"\n  -port string\n    \tthe port number to listen on (default \"3333\")\n````\n\nAt a minimum RegStat takes up to four arguments ...\n\n* the port number to listen on, provided using the `-port` option, defaults to 3333\n* the Postgres connection string, provided using the `-pg-conn-str` option, defaults to \"locahost:5432\" as user \"postgres\" with no password\n* registry authorization details, via a Docker `config.json` file, using the `-docker-config` option, defaults to none\n* an equivalent registries file, see *Equivalent registries* below, using the `-equiv-registries` option, defaults to none\n\nNote, be sure to quote the Postgres connection string.\n\nA full example ...\n````\n$ regstat -port 9999 \\\n-pg-conn-str \"host=localhost port=5432 user=regstat password=regstat dbname=regstat sslmode=disable\" \\\n-docker-config $HOME/.docker/config.json \\\n-equiv-registries $HOME/.docker/equiv-registries.json\n````\n\n## Registry authorization\n\nWhen processing the push of a Docker manifest, RegStat will make a RESTful call back to the registry to GET the\ncontents of that manifest. This allows RegStat to determine which blobs the manifest refers to, and so maintain\nthat information via the `manifest_blob` table.\n\nIf the registry requires the GET connection to be authorized then you must use the `-docker-config` option to\nprovide the path to a Docker `config.json` file that lists the appropriate authorization details for the\nregistry.\n\nRegStat supports both basic and brearer/token authorization methods.\n\n## Equivalent registries\n\nIt may be that one registry is known by different names. For instance clients may use different DNS aliases or\nIP addresses, or a default (80, 443) port number may or may not always be provided.\n\nThese equivalent registries create noise in the database: one image may be present multiple times in the\n`tags` table. To get around this you can create a *equivalent registries* JSON file that lists the preferred\nname for registries and any effective aliases for them.\n\nThe format of the file is ... \n\n````\n{\n  \"registry\" : [\n    \"alias1\",\n    \"alias2\",\n    ...\n  ],\n  \"another_registry\" : [\n    \"alias3\",\n    \"alias4\",\n    ...\n  ],\n  ...\n}\n````\n\nFor the example above any use of `alias1` or `alias2` will be mapped to `registry`, and use of `alias3` or `alias4`\nmapped to `another_registry`.\n\n## Configuring the Docker registry to notify RegStat\n\nSee the Docker documentation: [work with notifications](https://docs.docker.com/registry/notifications/).\n\nCurrently RegStat does not require any authorization tokens and listens on a HTTP port, rather than HTTPS.\n\nExample configuration ...\n\n````\nnotifications:\n  endpoints:\n    - name: RegStat\n      url: http://regstat.host:3333\n      timeout: 500ms\n      threshold: 5\n      backoff: 1s\n````\n\n## Building RegStat\n\nLinux static binary ...\n````\n$ go get github.com/vleurgat/regstat/cmd/regstat\n$ CGO_ENABLED=0 go install -a -ldflags '-extldflags=\"-static\"' github.com/vleurgat/regstat/cmd/regstat\n````\n\nWindows ...\n````\nC:\\\u003e go get github.com/vleurgat/regstat/cmd/regstat\nC:\\\u003e go install -a github.com/vleurgat/regstat/cmd/regstat\n````\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvleurgat%2Fregstat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvleurgat%2Fregstat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvleurgat%2Fregstat/lists"}