SixTwentyDOCS

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.

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.