Custom Container Images
Run your own container image for a game instead of the built-in Minecraft base image, including private-registry pull secrets.
By default a game runs the built-in Minecraft base image. Bring-Your-Own-Container (BYOC) lets a game run any image you build and push instead — a different engine, a custom runtime, or your own hardened base.
The supported loop is short: build and push an image, point the game at the new tag, and the next servers allocated for that game run it. You can set the image once from the web console, or automate the whole thing from CI.
The image reference
A game's image is a single field, customContainerUri: the full reference including registry,
repository, and tag.
ghcr.io/acme/my-game:1.4.2
Use immutable tags
Push every build under a new, immutable tag (a version or the commit SHA) — or pin a digest. The
platform keys off the reference string, so re-pushing over an existing tag (:latest) does not
roll running servers and is not reproducible. To ship a new build, push a new tag and update the
game to point at it.
Updating the image affects servers allocated after the change; it does not recreate servers that are already running.
Private registries: pull secrets
A pull secret holds the registry credentials used to pull a private image. It is owner-scoped (a user or organization) and reusable across that owner's games, so you create it once. Public images need no pull secret — skip this section.
Create one in the console at /console/pull-secrets, or via the API:
curl -X POST "https://control-plane.prod.620cloud.com/pull-secrets" \
-H "X-Auth-Token: $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"displayName": "acme-ghcr",
"registryServer": "ghcr.io",
"username": "acme-ci",
"password": "<registry-token>"
}'
registryServer is the registry host or URL (ghcr.io,
<acct>.dkr.ecr.<region>.amazonaws.com, https://index.docker.io/v1/). password may be a token
and is write-only — it is never returned. The response includes a canonical name, which is what
you reference from the game:
{
"name": "ps-u-507f1f77bcf86cd799439011-acme-ghcr",
"displayName": "acme-ghcr",
"registryServer": "ghcr.io",
"username": "acme-ci",
"createdAt": "2026-06-20T12:00:00Z"
}
Reference the name, not the display name
Use the returned name (here ps-u-…-acme-ghcr) wherever a game asks for imagePullSecretName.
A pull secret is only honored when the game also has a customContainerUri, and the secret and the
game must have the same owner (the same user or organization).
Pointing a game at an image
Set the image in the console — the Container section of a game's create or edit form takes a custom image URI and an optional pull secret — or call the update-project API directly:
curl -X PATCH "https://control-plane.prod.620cloud.com/projects/$PROJECT_ID" \
-H "X-Auth-Token: $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"customContainerUri": "ghcr.io/acme/my-game:1.4.2",
"imagePullSecretName": "ps-u-507f1f77bcf86cd799439011-acme-ghcr"
}'
Field semantics: omit a field to leave it unchanged; send an empty string "" for
customContainerUri to revert the game to the built-in Minecraft image (which also clears the pull
secret).
Automating from CI
A pull secret is reusable, so a deploy pipeline's per-build job only needs to build, push a new tag, and tell the game to use it. There are two ways to do that — the first is recommended.
Recommended: update the game
Set the image on the game with the update-project call. This is the authoritative value: it
persists on the game, works for public and private registries, supports a pull secret, and is the
same field the console writes. It is a management call, so it uses a
developer token in the X-Auth-Token header.
# .github/workflows/deploy.yml
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Log in to the registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/acme/my-game:${{ github.sha }}
- name: Point the game at the new image
run: |
curl -X PATCH "https://control-plane.prod.620cloud.com/projects/${{ vars.SIXTWENTY_PROJECT_ID }}" \
-H "X-Auth-Token: ${{ secrets.SIXTWENTY_AUTH_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{\"customContainerUri\": \"ghcr.io/acme/my-game:${{ github.sha }}\"}"
Token scope
A developer token is account-scoped, not project-scoped. Mint it from a dedicated CI/service account and rotate it.
Alternative: set the image when publishing a version
If your pipeline already publishes versions with a project-scoped
project secret and you would rather not store an account-scoped
developer token in CI, include customContainerUri in the publish request
(POST /projects/{projectId}/versions, authenticated with X-Project-Token) alongside your
uploadKey and versionSha.
This path is a backwards-compatible fallback, with limits to understand:
- It applies only to public images — it cannot attach a pull secret.
- It is not stored on the game.
- It is ignored if the game already has an image set on it; the game-level image always wins.
For anything beyond a public image, use the game-level setting above.
Reverting to the built-in image
Clear the custom image to put the game back on the built-in Minecraft base image (this also drops the pull secret):
curl -X PATCH "https://control-plane.prod.620cloud.com/projects/$PROJECT_ID" \
-H "X-Auth-Token: $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"customContainerUri": ""}'
See Configuration for the rest of a game's settings.