Docker Hub access tokens decide who can push the images your production clusters pull, and the images themselves are one of the most common places secrets leak. A personal access token with write permission can overwrite a trusted tag in every repository its owner can reach, and a token or API key copied into an image layer is published to anyone who runs docker pull. This guide covers both sides of the problem: how identity, roles and tokens work in Docker Hub, how to choose and scope personal and organisation access tokens, what to log and how to keep credentials out of the images you publish. It is written for platform engineers, DevSecOps teams and the administrators who own a Docker Hub organisation.
Key takeaways
- Use organisation access tokens for automation. OATs belong to the organisation, can be limited to specific repositories and scopes, and survive the departure of the person who created them; personal access tokens do not.
- PAT permissions are account-wide. A PAT has one repository permission that applies to every repository the account can access, so a "Read & Write" PAT for one pipeline can push to all of them.
- Expiry is not on by default. Both PATs and OATs default to "None" (never expire), and the expiry date cannot be changed later, so set 30 or 90 days at creation.
- Deleting a leaked secret from an image does not revoke it. Layers persist in every copy already pulled; revoke and reissue the credential, then rebuild.
- Build secrets belong in secret mounts. Docker's documentation says build arguments and environment variables persist in the final image; use
RUN --mount=type=secretinstead.
How identity and access work in Docker Hub
Docker Hub has user accounts, organisations that own repositories and teams, and, on Docker Business, companies that group several organisations. People sign in with a password, through SSO, or with a token. Automation should never use a password: once two-factor authentication is turned on or SSO is enforced, the CLI does not accept the account password at all and a token is required.
PATs and OATs compared
| Attribute | Personal access token (PAT) | Organisation access token (OAT) |
|---|---|---|
| Owner | A user account; acts as that user | The organisation; any organisation owner can view, edit, deactivate or delete it |
| Subscription | Available to individual accounts | Docker Team or Docker Business |
| Repository scope | One permission level across every repository the account can access | Up to 50 named repositories, or all organisation repositories, each with its own scopes |
| Other scopes | Optional Cloud Sandboxes access (sandbox:use) | Organisation scopes (members, audit log, repository creation and more) and product scopes |
| Expiry options | None (default), 30 days, 90 days, custom up to one year | None (default), 30 days, 90 days, custom up to one year |
| Sign-in username | The Docker username | The organisation name |
| Limits | Auto-generated tokens capped at five per account; fair use applies | 10 per organisation on Team, 100 on Business; expired and deactivated tokens count until deleted |
| Works with Docker Desktop and Image Access Management | Yes | No |
| Best for | A person using the CLI or Desktop | CI/CD, deployment and registry automation |
PAT permission levels
- Repo Public Read-only (default): view, search and pull public repositories.
- Repo Read-only: adds private repositories the account can access.
- Repo Read & Write: adds push to repositories the account manages.
- Repo Read, Write, Delete: adds deleting images and managing repositories.
- No repository access: for tokens used only for Cloud Sandboxes.
Permissions can be changed while a PAT is active or inactive, but not after it expires. Docker does not let you limit a PAT to a single repository; if a job needs that, it needs an OAT.
Admin roles and privileged access
| Role | Key permissions | Token relevance |
|---|---|---|
| Organisation owner | All organisation settings: members, roles, teams, SSO and SCIM, Image and Registry Access Management, billing; repository activity and publisher programmes | Can create, view, edit, deactivate and delete every OAT |
| Company owner | Same organisation management as an owner across the company; push and pull permissions do not apply | Can manage OATs for organisations in the company |
| Editor | Create, edit and delete repositories, manage tags, assign team permissions to repositories | A PAT from an editor with Read, Write, Delete can remove images across the organisation |
| Member | View members and pull from repositories the member can access | A member PAT is effectively read-only |
| Custom role (Business) | Granular organisation permissions, including "Manage organization access tokens" | Lets a platform team manage OATs without full ownership |
- Keep owners few: owners control SSO, SCIM and every OAT. Use two or three named owners and grant other administrators a custom role.
- Use teams for repository access: custom roles do not grant pull access to private repositories; team permissions do. Avoid making people editors just so a pipeline under their PAT can push.
- Enforce SSO and 2FA: with SSO enforced, the CLI requires a token, which removes reusable passwords from scripts. Before enforcing, Docker advises ensuring CI/CD pipelines use PATs or OATs instead of passwords.
- Remember the leaver problem: a PAT is tied to its creator. If a pipeline runs on an engineer's PAT, either it breaks when they leave or, worse, the account is kept alive to avoid the break. Move such pipelines to OATs. See the OWASP NHI Top 10 NHI10 Human Use of NHI Guide.
Integrations, API tokens and service accounts
OAT scopes worth knowing
OAT scopes are hierarchical: a more capable scope includes the lesser ones in its set.
| Scope (value) | Includes | Use it for |
|---|---|---|
Image Pull (scope-image-pull) | None | Deployment targets and clusters that only pull |
Image Push (scope-image-push) | Image Pull | The release job for a named repository |
Image Delete (scope-image-delete) | Image Push | Retention clean-up jobs only |
Tag Admin (scope-tag-admin) | Tag Read | Rarely needed by pipelines |
Webhook Admin (scope-webhook-admin) | Webhook Edit, Webhook Read | Integration set-up; a changed webhook can redirect events |
Repository Admin (scope-repository-admin) | Repository Edit, Repository Read | Administrative automation, not builds |
Repository Create (scope-repository-create, organisation scope) | None | Provisioning automation; no repository scope grants it |
Audit Log Read (scope-activity-read) | None | SIEM collectors pulling activity logs |
Member Edit (scope-member-edit) | Member Read | Identity automation; treat as privileged |
Choosing a token for each job
| Job | Token | Scope and expiry |
|---|---|---|
| CI build and push for one service | OAT | Image Push on that repository only, 90 days or less |
| Kubernetes image pull secret | OAT | Image Pull on the repositories the namespace runs; avoid "all organisation repositories" |
| Developer using the CLI with SSO enforced | PAT | Repo Read-only unless they publish by hand; 30 to 90 days |
| Docker Desktop or Image Access Management | PAT | OATs are not supported here |
| Log collection to a SIEM | OAT | Audit Log Read only |
| Individual account with no organisation | PAT per pipeline | Lowest permission level, one token per system so revocation is targeted |
Handling tokens on clients and runners
- Use
--password-stdin: it keeps the token out of shell history and logs. Never pass it with-pon the command line. - Configure a credential store: without one, Docker writes base64-encoded credentials into
~/.docker/config.json, which Docker itself describes as less secure. SetcredsStore(osxkeychain, wincred, pass or secretservice) or per-registrycredHelpers. - Clean up ephemeral runners: on self-hosted runners, run
docker logoutat the end of the job or use a fresh home directory, so the next job cannot read the previous job'sconfig.json. - Store tokens in a vault: the token is shown once and Docker does not store it. Record owner, purpose, scopes and expiry in your inventory at creation. See the Secrets Management Guide.
Image secret hygiene
Token scoping protects the registry. The other half of the problem is secrets carried inside the images it serves. The Docker Hub Secrets Leak 2025 found 10,456 images uploaded in a single month that exposed keys, most often AI model API keys, through packed .env files, hardcoded source, secrets written directly into Dockerfiles and whole project directories copied into the build. Earlier academic work, summarised in Secrets in Docker Hub Images, found secrets in about 8.5% of 337,171 images, including private keys still used by live TLS and SSH hosts.
| How secrets get in | Fix |
|---|---|
ARG or ENV holding a token | RUN --mount=type=secret,id=npm with docker build --secret id=npm,src=...; the secret is available only for that instruction |
| Private Git clone with an embedded token | SSH mount: docker buildx build --ssh default . |
COPY . . pulling in .env, .npmrc, .git or caches | .dockerignore listing secrets files, VCS folders and caches such as __pycache__ |
| Secret written then deleted in a later layer | Multi-stage build; copy only artefacts into the final stage |
| Runtime config baked into the image | Inject at runtime from an orchestrator or secrets manager |
| Work images pushed to personal or contractor namespaces | Require corporate namespaces; monitor public registries for your organisation's names and key patterns |
Scan the built image, not only the source. In the PyPI Admin GitHub Token Leak 2024, a token that was never committed reached public images through a compiled .pyc file because the .dockerignore did not exclude caches. See the Container Image Security Guide.
Logging and detection
Docker Hub activity logs are available on Team and Business. Docker Home keeps them for 90 days, the UI queries up to 30 days at a time, and the audit logs API returns up to three months of events in requests of up to 31 days. Export them to your SIEM with an OAT that holds only Audit Log Read, because 90 days is shorter than many exposure windows.
| Event | Why it matters | Response |
|---|---|---|
| Access token created or updated | New credential or widened scopes | Match against an approved change; check scopes |
| Access token revoked or deleted | Expected during rotation; unexpected deletion may hide tracks or break pipelines | Confirm owner action |
| Tag Pushed outside release windows | Possible push with a stolen token | Compare with CI run IDs and image digests |
| Privacy Changed | A private repository made public can publish embedded secrets | Alert immediately; verify intent |
| Repository Deleted or Tag Deleted | Destructive use of a delete-capable token | Restore from your build system; revoke the token |
| Tag immutability updated | Turning immutability off enables tag overwrite | Treat as a privileged change |
Also review the "Last used" column on PAT and OAT lists: tokens unused for 30 days are candidates for deletion, and use of a token outside its pipeline's schedule is worth investigating. Relevant ATT&CK techniques are T1528 Steal Application Access Token, T1552.001 Credentials In Files and T1195.002 Compromise Software Supply Chain.
Metrics that show it is working
- Automation on OATs: percentage of CI/CD and cluster pull credentials that are OATs rather than PATs.
- Expiry coverage: percentage of active tokens with an expiry of 90 days or less (target 100%).
- Least scope: number of OATs with "all organisation repositories" or Image Delete, trending down.
- Stale tokens: count of tokens not used in 30 days.
- Image secret findings: secrets detected in built images per month, and the share revoked (not just removed) within 24 hours.
- Log export continuity: days of activity log history held outside Docker Hub.
Hardening baseline
- Inventory: list every PAT and OAT, its creator, scopes, expiry and last used date, and map each to the pipeline or person that uses it.
- Enforce identity controls: turn on 2FA for all members and, on Business, configure and enforce SSO after confirming pipelines use tokens.
- Pilot OATs: move one service's build and pull credentials to OATs scoped to its repository, run for a release cycle, then revoke the old PAT. Keep the PAT deactivated (not deleted) for a week as a rollback path.
- Migrate by risk: production image pushes first, then cluster pull secrets, then everything else.
- Set expiry and rotation: 90 days or less for every token, with a rotation runbook; because expiry cannot be edited, rotation means creating a new token.
- Fix the build: adopt secret mounts, multi-stage builds and
.dockerignore, and add image secret scanning as a blocking gate before push. - Protect tags: enable tag immutability on release repositories and deploy by digest.
- Stream logs: export activity logs to the SIEM and alert on the events above.
Framework mapping
| Control area | NIST SP 800-53 Rev 5 | NIST CSF 2.0 | CIS Controls v8.1 | ISO/IEC 27001:2022 Annex A |
|---|---|---|---|---|
| Token issuance, expiry and storage | IA-5 Authenticator Management | PR.AA-01 | Control 5 Account Management | A.5.17 Authentication information |
| Scoped OATs, roles and teams | AC-6 Least Privilege | PR.AA-05 | Control 6 Access Control Management | A.8.2 Privileged access rights |
| Secret-free images and minimal build contexts | CM-7 Least Functionality | PR.PS-01 | Control 16 Application Software Security | A.8.9 Configuration management |
| Image integrity and trusted tags | SR-11 Component Authenticity | GV.SC-05 | Control 16 Application Software Security | A.5.21 Managing information security in the ICT supply chain |
| Activity log review | AU-6 Audit Record Review, Analysis, and Reporting | DE.AE-02 | Control 8 Audit Log Management | A.8.15 Logging |
Common misconfigurations
- Pipelines on a person's PAT: the token carries that person's access to every repository and dies, or lingers, with their account.
- Never-expiring tokens: "None" is the default for both token types. Tokens created years ago with Read, Write, Delete remain valid until someone deletes them.
- "All organisation repositories" on a pull secret: one leaked cluster secret then exposes every private image, and any secrets inside them.
- Removing the image, keeping the key: in the Flare study about a quarter of developers removed a leaked secret within a day or two, but most did not revoke it, so anyone who had pulled the image could still use it.
- Plain-text
config.jsonon shared runners: without a credential store, a token sits base64-encoded on disk for the next job or any malware on the host. - Short log retention: relying on 90 days of in-product history means a token leaked longer ago cannot be investigated, as PyPI found when GitHub's 90-day security log limit left the start of a 15-month image exposure unexplained.
Practitioner checklist
- Inventory every PAT and OAT with owner, scopes, expiry and last used date.
- Move CI/CD and cluster pull credentials from PATs to OATs scoped to named repositories.
- Set an expiry of 90 days or less on every new token and delete tokens unused for 30 days.
- Limit owners, use custom roles for token administration and teams for repository access.
- Enforce 2FA and, on Business, SSO once pipelines no longer use passwords.
- Configure credential stores and use
--password-stdinon every client and runner. - Use secret mounts, multi-stage builds and
.dockerignore, and scan built images before push. - Revoke and reissue any credential found in an image, then rebuild and republish.
- Export activity logs to the SIEM and alert on token changes, privacy changes and deletions.
Standards and references
- Docker Docs: Access tokens
- Docker Docs: Organization access tokens
- Docker Docs: Access token permissions and scopes reference
- Docker Docs: Core roles and permissions
- Docker Docs: Activity logs
- Docker Docs: Build secrets
- Docker Docs: docker login reference
- MITRE ATT&CK: T1552.001 Credentials In Files
Related NHI Mgmt Group resources: Container Registry Security Guide · Container Image Security Guide · npm Tokens Security Guide · Kubernetes NHI Security Guide · API Key Management Guide