Chapter 2. Programmatic OAuth token provisioning
To create and manage organization application tokens without the UI, you can use programmatic OAuth token provisioning through the REST API. You can also enable a Tech Preview bootstrap token for zero-touch automation.
Organization OAuth applications previously required administrators to create API tokens in the Red Hat Quay UI. With programmatic token provisioning, automation tools can manage the token life cycle.
When FEATURE_PROGRAMMATIC_BOOTSTRAP is enabled, Red Hat Quay also creates a high-privilege bootstrap OAuth token on startup and writes it to a local file or Kubernetes Secret. Use the bootstrap token to create organizations, applications, and narrower-scoped tokens without interactive UI access.
Programmatic bootstrap token provisioning is a Tech Preview feature in Red Hat Quay 3.18. Tech Preview features are not supported with Red Hat production service-level agreements (SLAs) and might not be functionally complete. Red Hat does not recommend using Tech Preview features in production environments. These features provide early access to upcoming product features, enabling customers to test functionality and provide feedback during the development process.
2.1. Programmatic bootstrap configuration fields Copy linkLink copied to clipboard!
Use these configuration fields to control bootstrap OAuth token provisioning for automated Red Hat Quay deployments. The feature remains disabled until you enable it. Standalone deployments store the token in a local file. Red Hat Quay on OpenShift Container Platform Operator deployments store the token in a Kubernetes Secret that the Operator creates and mounts.
| Field | Type | Description |
|---|---|---|
| FEATURE_PROGRAMMATIC_BOOTSTRAP | Boolean |
Enables programmatic bootstrap token provisioning. When
Default: |
| BOOTSTRAP_TOKEN_OWNER | String |
Username that owns the bootstrap OAuth application and token. Required when |
| BOOTSTRAP_TOKEN_PATH | String |
Filesystem path where the bootstrap token JSON is written on standalone and virtual machine deployments. In containerized deployments, use a mounted path that the Red Hat Quay process can write, for example
Default: |
| BOOTSTRAP_TOKEN_EXPIRATION | Integer | Bootstrap token lifetime in seconds.
Default: |
| BOOTSTRAP_TOKEN_SCOPE | String | Space-separated OAuth scopes assigned to the bootstrap token.
Default: |
| PROGRAMMATIC_TOKEN_K8S_SECRET | String |
Kubernetes Secret name used to store the bootstrap token when Red Hat Quay runs in Kubernetes. When set, Red Hat Quay writes the token to this Secret instead of |
| PROGRAMMATIC_TOKEN_K8S_KEY | String |
Secret data key that stores the bootstrap token JSON. On Red Hat Quay on OpenShift Container Platform, the Operator sets this field to
Default: |
| PROGRAMMATIC_TOKEN_K8S_NAMESPACE | String |
Kubernetes namespace that contains the bootstrap token Secret. When unset, Red Hat Quay uses the pod service account namespace. On Red Hat Quay on OpenShift Container Platform, the Operator uses the |
| PROGRAMMATIC_TOKEN_PATH | String |
Operator-rendered mount path for configuration compatibility. On Red Hat Quay on OpenShift Container Platform, the Operator sets this field to |
Programmatic bootstrap example YAML (standalone)
FEATURE_PROGRAMMATIC_BOOTSTRAP: true
SUPER_USERS:
- quayadmin
BOOTSTRAP_TOKEN_OWNER: quayadmin
BOOTSTRAP_TOKEN_PATH: /var/lib/quay/quay-machine-token.json
BOOTSTRAP_TOKEN_EXPIRATION: 7776000
BOOTSTRAP_TOKEN_SCOPE: "org:admin repo:admin repo:create repo:read repo:write super:user user:admin user:read"
Programmatic bootstrap example YAML (Red Hat Quay on OpenShift Container Platform configBundleSecret)
FEATURE_PROGRAMMATIC_BOOTSTRAP: true
SUPER_USERS:
- quayadmin
BOOTSTRAP_TOKEN_OWNER: quayadmin
BOOTSTRAP_TOKEN_EXPIRATION: 7776000
BOOTSTRAP_TOKEN_SCOPE: "org:admin repo:admin repo:create repo:read repo:write super:user user:admin user:read"
For Operator deployments, omit BOOTSTRAP_TOKEN_PATH and the PROGRAMMATIC_TOKEN_K8S_* fields from the configBundleSecret. The Operator injects PROGRAMMATIC_TOKEN_K8S_SECRET, PROGRAMMATIC_TOKEN_K8S_KEY, and PROGRAMMATIC_TOKEN_PATH, and creates the <quayregistry_name>-bootstrap-token Secret, Role, and RoleBinding automatically.
Always quote BOOTSTRAP_TOKEN_SCOPE. Unquoted values that contain : can be misparsed by YAML.
The bootstrap token is a high-privilege credential. Use it only to provision organizations, applications, and narrower-scoped OAuth tokens for automation. Do not use the bootstrap token for routine API operations.
For regulated environments, set BOOTSTRAP_TOKEN_EXPIRATION according to your token rotation policy and renew the token by using POST /api/v1/bootstrap/renew.
2.2. Configuring programmatic bootstrap on standalone deployments Copy linkLink copied to clipboard!
To configure filesystem-based bootstrap OAuth token provisioning in a standalone Red Hat Quay deployment, you can set FEATURE_PROGRAMMATIC_BOOTSTRAP and related fields in the config.yaml file, then restart the registry.
Prerequisites
- You have a standalone Red Hat Quay deployment with at least one superuser account created.
-
You can modify the Red Hat Quay
config.yamlfile.
Procedure
Set the following fields in your Red Hat Quay
config.yamlfile:FEATURE_PROGRAMMATIC_BOOTSTRAP: true SUPER_USERS: - quayadmin BOOTSTRAP_TOKEN_OWNER: quayadmin BOOTSTRAP_TOKEN_EXPIRATION: 7776000 BOOTSTRAP_TOKEN_SCOPE: "org:admin repo:admin repo:create repo:read repo:write super:user user:admin user:read" BOOTSTRAP_TOKEN_PATH: /datastorage/bootstrap-token.jsonSet
BOOTSTRAP_TOKEN_PATHto a directory that the Red Hat Quay process can write. In containerized standalone deployments, use a mounted storage path such as/datastorage/bootstrap-token.json. Quote theBOOTSTRAP_TOKEN_SCOPEvalue so YAML does not misparse scopes that contain:.Restart Red Hat Quay after you update the configuration.
NoteIf you enable
FEATURE_PROGRAMMATIC_BOOTSTRAPon a deployment that is already running, you must restart Red Hat Quay so the bootstrap token is provisioned and thePOST /api/v1/bootstrap/renewendpoint is registered. A full restart is required; reloading configuration without restarting does not register the bootstrap API endpoints.Verify bootstrap provisioning:
-
Check Red Hat Quay startup logs for a
Bootstrap token provisionedmessage. Confirm that the bootstrap token file exists at the configured storage location. For example:
$ ls -l <BOOTSTRAP_TOKEN_PATH>
-
Check Red Hat Quay startup logs for a
2.3. Configuring programmatic bootstrap on OpenShift Container Platform Copy linkLink copied to clipboard!
To enable filesystem-independent bootstrap OAuth token provisioning for Red Hat Quay on OpenShift Container Platform, you can add the programmatic bootstrap fields to the configBundleSecret resource. The Red Hat Quay Operator creates the bootstrap token Secret, Role, and RoleBinding, injects the Kubernetes storage fields, and restarts the Quay pods.
Programmatic bootstrap token provisioning is a Tech Preview feature in Red Hat Quay 3.18. Tech Preview features are not supported with Red Hat production service-level agreements (SLAs) and might not be functionally complete. Red Hat does not recommend using Tech Preview features in production environments. These features provide early access to upcoming product features, enabling customers to test functionality and provide feedback during the development process.
Prerequisites
- You have deployed a Red Hat Quay registry on OpenShift Container Platform by using the Red Hat Quay Operator.
- You have created at least one superuser account. For more information, see Creating the first user.
-
You can edit the
configBundleSecretresource that is referenced by yourQuayRegistrycustom resource (CR).
Procedure
Retrieve the name of the
configBundleSecretresource:$ oc get quayregistry <quayregistry_name> -n <quay_namespace> \ -o jsonpath='{.spec.configBundleSecret}{"\n"}'Example output
example-registry-config-bundle-abc12Export the current
config.yamlfile from the secret:$ oc get secret -n <quay_namespace> <config_bundle_secret_name> \ -o jsonpath='{.data.config\.yaml}' | base64 -d > config.yamlEdit
config.yamland add the programmatic bootstrap fields. For example:FEATURE_PROGRAMMATIC_BOOTSTRAP: true SUPER_USERS: - quayadmin BOOTSTRAP_TOKEN_OWNER: quayadmin BOOTSTRAP_TOKEN_EXPIRATION: 7776000 BOOTSTRAP_TOKEN_SCOPE: "org:admin repo:admin repo:create repo:read repo:write super:user user:admin user:read"Important-
BOOTSTRAP_TOKEN_OWNERmust be an existing superuser that is also listed underSUPER_USERS. -
Quote the
BOOTSTRAP_TOKEN_SCOPEvalue. Unquoted scope strings that contain:can be misparsed by YAML. -
Do not set
BOOTSTRAP_TOKEN_PATHfor Operator deployments. The Operator stores the token in a Kubernetes Secret. -
You do not need to set
PROGRAMMATIC_TOKEN_K8S_SECRET,PROGRAMMATIC_TOKEN_K8S_KEY, orPROGRAMMATIC_TOKEN_K8S_NAMESPACE. The Operator injects those values and creates the Secret named<quayregistry_name>-bootstrap-token.
-
Create a new config bundle secret that includes the updated
config.yamlfile:$ oc create secret generic <new_config_bundle_secret_name> \ --from-file=config.yaml=./config.yaml \ -n <quay_namespace>Update the
QuayRegistryCR to reference the new secret:$ oc patch quayregistry <quayregistry_name> -n <quay_namespace> \ --type=merge -p '{"spec":{"configBundleSecret":"<new_config_bundle_secret_name>"}}'The Operator reconciles the change, creates the
<quayregistry_name>-bootstrap-tokenSecret with accompanying Role and RoleBinding resources, mounts the Secret into the Quay application pods, and restarts Quay-related pods.Wait for the Quay application pods to become ready:
$ oc get pods -n <quay_namespace> -l quay-component=quay-appVerify that the Operator created the bootstrap token Secret:
$ oc get secret <quayregistry_name>-bootstrap-token -n <quay_namespace>Read the bootstrap token from the Secret:
$ BOOTSTRAP_TOKEN=$(oc get secret <quayregistry_name>-bootstrap-token -n <quay_namespace> \ -o jsonpath='{.data.token\.json}' | base64 -d | jq -r '.access_token')NoteSecret propagation can take up to 60 seconds after the Quay pods start. If
token.jsonis missing, wait and retry the command.Optional. Confirm that Quay startup logs include a bootstrap provisioning message:
$ oc logs -n <quay_namespace> deploy/<quayregistry_name>-quay-app -c quay-app \ | grep -i 'bootstrap token'
2.4. Reading the bootstrap token Copy linkLink copied to clipboard!
To obtain the bootstrap OAuth token after Red Hat Quay starts with programmatic bootstrap enabled, you can read the token from the configured local file or Kubernetes Secret.
Procedure
For standalone or virtual machine deployments, read the token from the
BOOTSTRAP_TOKEN_PATHfile:$ BOOTSTRAP_TOKEN=$(jq -r '.access_token' /var/lib/quay/quay-machine-token.json)NoteRed Hat Quay writes the bootstrap token file with
0600permissions owned by the Red Hat Quay process user. If you cannot read the file from the host, read it from inside the Red Hat Quay container instead. For example:$ BOOTSTRAP_TOKEN=$(docker exec quay cat /datastorage/bootstrap-token.json | jq -r '.access_token')For Red Hat Quay on OpenShift Container Platform Operator deployments, read the token from the Operator-managed Secret. The Secret name is
<quayregistry_name>-bootstrap-token:$ BOOTSTRAP_TOKEN=$(oc get secret <quayregistry_name>-bootstrap-token -n <quay_namespace> \ -o jsonpath='{.data.token\.json}' | base64 -d | jq -r '.access_token')Example
$ BOOTSTRAP_TOKEN=$(oc get secret example-registry-bootstrap-token -n quay-operator \ -o jsonpath='{.data.token\.json}' | base64 -d | jq -r '.access_token')NoteSecret propagation can take up to 60 seconds after renewal or initial provisioning. If the
token.jsonkey is missing, wait for the Quay application pods to finish starting and retry the command.
2.5. Using the bootstrap token for zero-touch deployment Copy linkLink copied to clipboard!
To provision Red Hat Quay resources without using the UI, you can use the bootstrap OAuth token as Bearer authentication for organization, application, and token API calls.
Procedure
Export the bootstrap token. For example:
$ export BOOTSTRAP_TOKEN=<bootstrap_token_value>Create an organization:
$ curl -H "Authorization: Bearer $BOOTSTRAP_TOKEN" -X POST \ -H "Content-Type: application/json" \ -d '{"name": "myorg", "email": "admin@example.com"}' \ https://<quay-server.example.com>/api/v1/organization/Create an OAuth application in the organization:
$ curl -H "Authorization: Bearer $BOOTSTRAP_TOKEN" -X POST \ -H "Content-Type: application/json" \ -d '{"name": "ci-automation", "description": "CI/CD token source"}' \ https://<quay-server.example.com>/api/v1/organization/myorg/applicationsCreate a scoped OAuth API token for the application:
$ curl -H "Authorization: Bearer $BOOTSTRAP_TOKEN" -X POST \ -H "Content-Type: application/json" \ -d '{"name": "ci-job-token", "scope": "repo:read repo:write", "expiration": 2592000}' \ https://<quay-server.example.com>/api/v1/organization/myorg/applications/<client_id>/tokens-
Store the
tokenvalue from the response for your automation workflow. The token secret is returned only in the create response.
2.6. Managing OAuth application tokens by using the API Copy linkLink copied to clipboard!
To manage organization application OAuth tokens without using the UI, you can list, create, and revoke tokens through the Red Hat Quay REST API when your token has org:admin scope.
The bootstrap token includes org:admin by default. Scoped tokens that you create for automation must also include org:admin to list, create, or revoke application tokens through these endpoints.
Procedure
List existing tokens for an application:
$ curl -H "Authorization: Bearer <access_token>" -X GET \ https://<quay-server.example.com>/api/v1/organization/myorg/applications/<client_id>/tokensCreate a token with a custom expiration and scope:
$ curl -H "Authorization: Bearer <access_token>" -X POST \ -H "Content-Type: application/json" \ -d '{"name": "short-lived-token", "scope": "repo:read", "expiration": 3600}' \ https://<quay-server.example.com>/api/v1/organization/myorg/applications/<client_id>/tokensRevoke a token by UUID:
$ curl -H "Authorization: Bearer <access_token>" -X DELETE \ https://<quay-server.example.com>/api/v1/organization/myorg/applications/<client_id>/tokens/<token_uuid>
2.7. Renewing the bootstrap token Copy linkLink copied to clipboard!
To keep automation running when the bootstrap OAuth token approaches expiry, you can renew it through the Red Hat Quay REST API before the previous token is invalidated.
Procedure
Renew the token by using the bootstrap token as Bearer authentication:
$ curl -H "Authorization: Bearer $BOOTSTRAP_TOKEN" -X POST \ https://<quay-server.example.com>/api/v1/bootstrap/renewThe following example shows a successful response:
{"status": "rotated"}Read the new token value from
BOOTSTRAP_TOKEN_PATHor the configured Kubernetes Secret. The previous bootstrap token is invalidated immediately.NoteIf the bootstrap token is already expired, renewal is accepted only from localhost. On Kubernetes and OpenShift Container Platform, use port forwarding to send the renewal request through the local ingress path.
2.8. Revoking the bootstrap token Copy linkLink copied to clipboard!
You can revoke the Red Hat Quay bootstrap OAuth token by disabling FEATURE_PROGRAMMATIC_BOOTSTRAP and restarting the registry. Red Hat Quay does not provide a separate API endpoint for instant revocation.
To revoke the bootstrap token, set FEATURE_PROGRAMMATIC_BOOTSTRAP: false and restart Red Hat Quay. Red Hat Quay deletes bootstrap-managed applications and tokens during startup.
2.9. Security considerations for programmatic bootstrap Copy linkLink copied to clipboard!
Apply these practices when you use the Red Hat Quay bootstrap OAuth token so that automation remains limited to provisioning and uses narrower-scoped tokens for day-to-day work.
- Use the bootstrap token only for initial provisioning and token minting. Create narrower-scoped OAuth tokens for CI/CD and day-2 automation.
-
Set
BOOTSTRAP_TOKEN_EXPIRATIONaccording to your security policy. The default is 60 minutes. -
On standalone deployments, the bootstrap token file is written with
0600permissions. Restrict access to the directory that contains the token file. - On Kubernetes and OpenShift Container Platform, store the bootstrap token in a dedicated Secret with scoped RBAC.
-
Schedule bootstrap token renewal by using
POST /api/v1/bootstrap/renewbefore expiry. - Monitor Red Hat Quay action logs for bootstrap and OAuth token life cycle events.
2.10. Troubleshooting programmatic bootstrap Copy linkLink copied to clipboard!
Use these checks when programmatic bootstrap token provisioning fails in Red Hat Quay, including missing token files, authorization errors, and renewal failures.
If the token file or Secret is empty after startup:
-
Verify that
FEATURE_PROGRAMMATIC_BOOTSTRAPistrue. -
Verify that
BOOTSTRAP_TOKEN_OWNERis set and listed inSUPER_USERS. - Verify that the bootstrap token owner exists in the Red Hat Quay database.
- Check Red Hat Quay startup logs for bootstrap provisioning errors.
-
If the bootstrap token file or Secret is still missing after the first restart, restart Red Hat Quay again or run
python3 /quay-registry/boot.pyinside the Red Hat Quay container after confirmingBOOTSTRAP_TOKEN_OWNERexists in the database. -
On Red Hat Quay on OpenShift Container Platform, confirm that the Operator created the
<quayregistry_name>-bootstrap-tokenSecret, Role, and RoleBinding, and that Quay application pods have rolled out with the updatedconfigBundleSecret. An empty Secret before the pods restart is expected; the Quay process writestoken.jsonafter startup. -
Confirm that
BOOTSTRAP_TOKEN_SCOPEis a quoted YAML string. Unquoted scope values that contain:can be misparsed.
If you receive 403 Forbidden when using the bootstrap token:
- Confirm that the token has not expired.
- Confirm that bootstrap provisioning was not disabled and the token revoked.
- Confirm that the target API endpoint is authorized by the bootstrap token scope.
If renewal returns 401 Unauthorized:
- If the token is expired, send the renewal request from localhost or through a port-forwarded local ingress path.
- Confirm that you are passing the bootstrap token value, not a different OAuth token.
If rate limiting returns 429 Too Many Requests:
-
Bootstrap and token management endpoints are subject to existing rate limiting when
FEATURE_RATE_LIMITSis enabled. Adjust request frequency or review your rate limit configuration.