Getting started with GitHub Container Registry (GHCR)
Use this guide if your game-server image lives in GitHub Container Registry
(ghcr.io) instead of Docker Hub. By the end, a Gameye session runs your private
GHCR image, and every new tag you push reaches Gameye without manual steps.
This page replaces 1. Set up Docker Hub. 2. Create your Dockerfile still applies.
Before you start
Section titled “Before you start”You need:
- A GitHub repository with a
Dockerfilefor your game server. The image must supportlinux/amd64. - Admin access to that repository and its package on GitHub.
- A Gameye Admin Panel login that can create applications and API tokens.
- Your Gameye organization name. It is shown in the Organization field when you create an application.
The examples use the image ghcr.io/studio-name/game-server and the
application name my-game-server.
1. Publish your image to GHCR
Section titled “1. Publish your image to GHCR”Publish from GitHub Actions with the built-in GITHUB_TOKEN. You do not need a
personal access token. Save this workflow as
.github/workflows/gameye-ghcr.yml in the repository that holds your
Dockerfile:
name: Publish game server to GHCR
on: push: branches: [main] workflow_dispatch:
permissions: contents: read packages: write
jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Choose a unique tag id: image shell: bash run: | repository="${GITHUB_REPOSITORY,,}" tag="build-${GITHUB_RUN_NUMBER}-${GITHUB_RUN_ATTEMPT}-${GITHUB_SHA::7}" echo "ref=ghcr.io/${repository}:${tag}" >> "$GITHUB_OUTPUT" - uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64 push: true provenance: false tags: ${{ steps.image.outputs.ref }} labels: | org.opencontainers.image.source=https://github.com/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }}Every run publishes a new, unique tag such as build-12-1-3f9c2ab. Use a new tag
for every build: Gameye nodes cache images by tag, so pushing latest or another
existing tag again does not make Gameye download the new image.
Keep provenance: false. By default this action attaches a build attestation to
the image, and GitHub sends no package webhook for a push that includes one, so
new tags would never register automatically in step 5.
The workflow publishes to ghcr.io/<repository owner>/<repository name>, and
the org.opencontainers.image.source label connects the package to the
repository. You can also publish from your own
machine with docker push, as described in
GitHub’s Container registry guide.
2. Give Gameye Read access to the package
Section titled “2. Give Gameye Read access to the package”Gameye pulls private GHCR images with its GitHub account gameyedocker. Give
that account Read access to the package. You never share a GitHub token with
Gameye.
- If the package belongs to a GitHub organization: invite
gameyedockerto the organization under Organization → People → Invite member, then ask Gameye support to accept the invitation. Organization members receive your organization’s base repository permission, so check it under Settings → Member privileges first. Skip this step for a package owned by a personal account. - Open the package on GitHub (your profile or organization → Packages) and go to Package settings.
- If Inherit access from source repository is ticked, untick it. Until you do, Manage access has no Invite teams or people button.
- Under Manage access, select Invite teams or people, add
gameyedocker, and set its role to Read.
The package can stay private. Granting access to the package alone does not give Gameye access to your repository’s code. See GitHub’s package access guide for details.
3. Create the application in the Admin Panel
Section titled “3. Create the application in the Admin Panel”- In the Admin Panel, open Applications and click Add.
- On the Create New Application page, fill in:
| Field | Value |
|---|---|
| Application Name | A name for the application, for example my-game-server. You use it as image when you start sessions. |
| Repository URL | ghcr.io/studio-name/game-server, all lowercase, with no https:// and no tag. |
| Image Registry | ghcr |
| Organization | Your organization. It must match the owner in Repository URL. |
| Node Pool | The node pool assigned to you. If unsure, ask your administrator. |
- Fill in regions, port bindings, and CPU/RAM limits as described in 3. Set up the Admin Panel, then click Create Application.
Type Repository URL exactly as shown. Gameye matches new tags and pull
permissions against this exact text, so a capital letter, an https:// prefix,
or a tag stops images from loading.
You can also create the application with
POST /application, setting
"registry": "ghcr" and "repository": "ghcr.io/studio-name/game-server".
4. Load your first tag
Section titled “4. Load your first tag”Your first image was probably published before the application existed, so add its tag by hand. Either:
- Admin Panel: open Tags, find your application’s card, and click Add Tag. The dialog lists the package’s tags. Select your tag and click Add “<tag>”.
- API: call
POST /application/{name}/tagswith a token that has theapplication:writeandregions:readscopes. A successful call returns HTTP 204.
curl -X POST 'https://api.sandbox-gameye.gameye.net/application/my-game-server/tags' \ --header 'Authorization: Bearer YOUR_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "tag": "build-12-1-3f9c2ab" }'The tag appears on the Tags page as Preloading while nodes download the
image, then changes to Ready. To check from a script, call
GET /tag/{region}/{image}/{version} and wait
for {"exists":true}.
5. Register new tags automatically
Section titled “5. Register new tags automatically”A GitHub webhook tells Gameye about every tag you push, so you can skip step 4 for future builds. GitHub signs each delivery with a secret, and that secret is a Gameye API token used only for this webhook.
Create the webhook secret
Section titled “Create the webhook secret”Create a token with only the webhook:github scope:
-
Admin Panel: open Users, edit your user, and in API Tokens click + Add Token. Name it (for example
github-webhook), tick onlywebhook:github, and click Create Token. -
API: call
POST /tokenwith a token that hastoken:write. It does not needwebhook:githubitself.Terminal window curl -X POST 'https://api.sandbox-gameye.gameye.net/token' \--header 'Authorization: Bearer YOUR_TOKEN' \--header 'Content-Type: application/json' \--data '{ "name": "github-webhook", "scopes": ["webhook:github"] }'
Copy the token value. It is shown in full only once. Do not give this token any other scope, because it is stored in GitHub.
Add the webhook in GitHub
Section titled “Add the webhook in GitHub”Ask Gameye support for your webhook URL. It contains your Gameye organization ID and looks like this:
https://api.sandbox-gameye.gameye.net/v1/integrations/github/<ORGANIZATION-ID>In the repository from step 1, open Settings → Webhooks → Add webhook. To cover every package in a GitHub organization instead, add the webhook under the organization’s Settings → Webhooks. Set:
| Field | Value |
|---|---|
| Payload URL | Your webhook URL |
| Content type | application/json |
| Secret | The webhook:github token |
| Which events would you like to trigger this webhook? | Let me select individual events, then tick only Packages |
| Active | Ticked |
Click Add webhook. GitHub sends a ping, which should show 200 under Recent Deliveries.
How long a new tag takes
Section titled “How long a new tag takes”When a push creates a new package version, GitHub sends a delivery. GitHub can
take a few minutes to send it. Gameye registers the tag as soon as the delivery
arrives, and the tag then appears on the Tags page as Preloading until
nodes have downloaded it. Use the tag only after it shows Ready, or after
GET /tag returns {"exists":true}.
GitHub sends nothing when you push an image identical to one already in the
package, because no new version is created. The workflow in step 1 sets the
org.opencontainers.image.revision label to the commit SHA, so each commit
builds a new version. Otherwise, add the tag by hand as in step 4.
6. Start a session
Section titled “6. Start a session”Start a session with POST /session.
Set image to your application name and version to the tag. The token needs
the session:start scope.
curl -X POST 'https://api.sandbox-gameye.gameye.net/session' \ --header 'Authorization: Bearer YOUR_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "location": "europe", "image": "my-game-server", "version": "build-12-1-3f9c2ab" }'Use a region your application is configured for as location. Connect to the
returned host and port to check your server, then
stop the session. See
4. Start with the API for the full session
flow.
Troubleshooting
Section titled “Troubleshooting”Images do not load
Section titled “Images do not load”| Symptom | Fix |
|---|---|
| Invite teams or people is missing in Manage access | The package inherits access from its repository. Untick Inherit access from source repository in Package settings. |
| Add Tag shows No tags available | Most often Repository URL is mistyped. The Admin Panel saves it without checking, so a typo, a capital letter, an https:// prefix or a tag on the end is accepted when you create the application and only shows up here. Open the application and compare Repository URL with the package name on GitHub, character by character: it must be exactly ghcr.io/<owner>/<package>, all lowercase. Then check that gameyedocker has Read on the package. |
| The tag stays Preloading | Check, in order: gameyedocker has Read on the package (and, for an organization package, has accepted the organization invitation); the owner in Repository URL is your Gameye organization name (the application page warns when it is not; a private package with a different owner never loads); Repository URL is lowercase with no tag or https://; the image supports linux/amd64. If it is still stuck, contact Gameye support with the application name and tag. |
POST /session returns 404 | The tag is not available in that region yet. Wait for Ready, or for GET /tag to return {"exists":true}. |
| A session runs an old build | You pushed an existing tag again. Publish a new, unique tag. |
Webhook deliveries
Section titled “Webhook deliveries”Open the webhook in GitHub and check Recent Deliveries. Each delivery shows Gameye’s response code and body.
| Response | Meaning |
|---|---|
| 204 | The tag is registered and nodes start downloading it. |
| 200 | Nothing to do. The body gives the reason: a ping, an event other than a package publish, a non-container package, an untagged push, or a tag that is already registered. |
| 200 mentioning no application for the repository | No GHCR application in your organization has this exact Repository URL. Check spelling and lowercase. A later push registers tags once the application exists. |
| 400 | The body is not JSON. Set Content type to application/json. The ping still succeeds with the wrong content type, so check a package delivery. |
| 401 | The signature did not match. Check that Secret is an active webhook:github token from the organization in the webhook URL, and that it has not been disabled or deleted. |
| No delivery after a push | Wait a few minutes. Check that the build sets provenance: false (or pushes with plain docker push): GitHub sends no package webhook for an image pushed with a build attestation, which docker/build-push-action adds by default. Check that Packages is the selected event and the webhook is Active. For a repository webhook, the package must be connected to that repository, which the org.opencontainers.image.source label does. An identical image creates no delivery. |
To rotate the webhook secret, create a new webhook:github token, paste it into
the webhook’s Secret in GitHub, then delete the old token under
Users → API Tokens. Both tokens work until
you delete the old one.