Pushing Images to the Registry

How to log in to repo.pingcore.io, where your images live, and how to tag them so a rollout is deliberate.

Game server images run from repo.pingcore.io, the registry the platform pulls from. Other registries are refused when you save the image row unless the operator has allowed them, so an image on Docker Hub or on your own registry does not work by default.

Log in and push

docker login repo.pingcore.io
docker tag yourstudio/your-game:dev repo.pingcore.io/<your-project>/your-game:2026.09.15-abc123
docker push repo.pingcore.io/<your-project>/your-game:2026.09.15-abc123

<your-project> is a registry namespace assigned to your brand. Your repository path has to start with one of them, matched by path segment, so acme covers acme/your-game but not acmeworks/your-game. Namespaces and registry credentials are issued by support at the moment. Ask for them before your first build, because a brand with no namespace cannot create images of its own.

Add the image row

Go to Workspace > Games > Container Images and create an image with:

  • Registry: repo.pingcore.io

  • Repository: the path under your namespace, for example acme/your-game

  • Tag: the tag you pushed

The form shows the assembled Full path under the fields. Select the image on the game's main container, under Workspace > Games, open the game, Containers.

Tag one immutable tag per build

Pods run with imagePullPolicy: Always, so a pod fetches whatever its tag currently points at every time it is created.

Use a tag that identifies the build and never gets reused, such as :2026.09.15-abc123. Rolling a fleet onto a new build is then two deliberate steps: change the Tag on the image row, then restart the deployment.

A running server keeps the image it started with until its pod is recreated. The platform recreates a pod on its own for several reasons:

  • the game crashes, or the memory governor restarts it

  • the pod is killed out of memory

  • node maintenance, a drain, or a migration to another node

  • a Game Swap wake

  • autoscaling replacing or adding instances

  • a deployment restart

A moving tag such as :latest turns every one of those into an unplanned upgrade. A fleet can end up running two builds at once, with no way to roll a single server back. The platform allows it. Use an immutable tag instead.

Tags and .version are independent

The image tag decides which image a pod pulls. The .version file inside the image decides whether the supervisor recopies the game onto a server's data volume.

  • A new tag with the same .version pulls the new image but does not recopy onto a persistent volume, so servers keep the game files they already have.

  • A new tag with a different .version recopies. The comparison is inequality, not ordering, so any change counts.

Bump .version whenever the game files change. Leave it alone when you only changed something outside the game tree, such as a runtime package. Baked-In Game Files covers what the copy does to files already on the volume.

Windows generation pairing

A Windows image has to be built on the same Windows Server Core generation as the node pool it runs on, because a Windows container cannot run on an older host kernel. Build against ltsc2025 unless support has told you your servers are on ltsc2022 nodes. See Supervisor Base Images.

Large images

Game builds make large images:

  • Image layers live in the node's image store and do not count against the server's ephemeral-storage bound. The copy the supervisor makes onto the data volume does.

  • A node holds your image once, plus one full copy of the game per server running there.

  • A cold pull of a multi-GB image takes minutes, and the platform allows for that after a deployment.

What happens after a deploy

Each new server gets 10 minutes on a Linux branch, or 15 on Windows, to answer the supervisor's /health from the moment it is provisioned. That clock is paused while the pod is demonstrably still pulling or creating its container, so a slow pull does not count against it. Nothing waits longer than 40 minutes in total.

A server that never reports healthy is removed and the deployment's job records the reason:

  • image_pull: the server or setup container image could not be pulled, with the Kubernetes reason (ImagePullBackOff, ErrImagePull or InvalidImageName) in the message.

  • container_start: the image pulled but the container could not run. The message reads "The image does not contain the PingCore supervisor, or it failed to start."

  • crash_loop: the container starts and dies repeatedly, with the same message.

  • supervisor_error: the supervisor started and reported its own install or boot error, quoted in the message.

  • pull_timeout: the image was still being pulled after 40 minutes.

  • unschedulable: "No node in the cluster can fit this server's resource requests." Not reported today: the controller does not yet pass the scheduling condition through, so a request no node can fit currently ends as timeout.

  • timeout: the server did not report healthy within its 10 or 15 minute bound, and nothing else explained why.

Check the job first when a deployment reports fewer healthy servers than expected.