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.
Claim a namespace
Go to Workspace > Content > Container Registry. A namespace is your own private project on the registry: every image you push lives under it, and only servers in your workspace can pull from it.
Type a name and claim it. Names are 5 to 20 characters of lowercase letters, numbers and single hyphens, with at least one letter (the registry would read an all-digit name as a project number rather than a name). The form checks against the registry as you type, so you see straight away whether a name is free. Pick carefully: a namespace cannot be renamed or released once claimed, because the registry does not allow either.
The registry is off for a workspace until support enables it, and the number of namespaces you can claim is set per workspace. One is enough for most studios. If the page says the registry is not enabled, ask support to turn it on.
Issue a push credential
On the same page, under Push credentials, give the credential a short label such as ci or build-server and issue it. You get a username and a secret. The secret is shown exactly once, so put it in your CI secret store before you dismiss it. If it is lost, rotate the credential: the username stays the same and only the secret changes.
A credential can push to and pull from the namespaces your workspace had when it was issued, and do nothing else. Issue one per pipeline or developer so you can revoke them independently. If you claim another namespace later, existing credentials do not cover it until you rotate them; the page marks the ones that need it, and rotating keeps the username.
Log in and push
docker login repo.pingcore.io -u 'robot$pc-b12-ci'
docker tag yourstudio/your-game:dev repo.pingcore.io/<your-namespace>/your-game:2026.09.15-abc123
docker push repo.pingcore.io/<your-namespace>/your-game:2026.09.15-abc123Paste the secret when docker login prompts for a password. The username is the full robot name the page shows, including the robot$ prefix, so quote it in a shell. Your repository path has to start with one of your namespaces, matched by path segment, so acme-games covers acme-games/your-game but not acme-gamesx/your-game. A workspace with no namespace cannot create images of its own.
Add the image row
Go to Workspace > Content > Container Images and create an image with:
Registry:
repo.pingcore.ioRepository: the path under your namespace, for example
acme-games/your-gameTag: 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.
Tags in your brand's registry namespace are immutable, so a push that reuses an existing tag is refused. Give every build its own tag.
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 recopies onto a persistent volume, because PingCore tells each container which tag it is running and a changed tag is enough on its own.
A changed
.versionalso 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,ErrImagePullorInvalidImageName) 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.