Skip to content

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.

You need:

  • A GitHub repository with a Dockerfile for your game server. The image must support linux/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.

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.

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.

  1. If the package belongs to a GitHub organization: invite gameyedocker to 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.
  2. Open the package on GitHub (your profile or organization → Packages) and go to Package settings.
  3. If Inherit access from source repository is ticked, untick it. Until you do, Manage access has no Invite teams or people button.
  4. 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”
  1. In the Admin Panel, open Applications and click Add.
  2. On the Create New Application page, fill in:
FieldValue
Application NameA name for the application, for example my-game-server. You use it as image when you start sessions.
Repository URLghcr.io/studio-name/game-server, all lowercase, with no https:// and no tag.
Image Registryghcr
OrganizationYour organization. It must match the owner in Repository URL.
Node PoolThe node pool assigned to you. If unsure, ask your administrator.
  1. 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".

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}/tags with a token that has the application:write and regions:read scopes. A successful call returns HTTP 204.
Terminal window
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}.

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 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 only webhook:github, and click Create Token.

  • API: call POST /token with a token that has token:write. It does not need webhook:github itself.

    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.

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:

FieldValue
Payload URLYour webhook URL
Content typeapplication/json
SecretThe webhook:github token
Which events would you like to trigger this webhook?Let me select individual events, then tick only Packages
ActiveTicked

Click Add webhook. GitHub sends a ping, which should show 200 under Recent Deliveries.

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.

Start a session with POST /session. Set image to your application name and version to the tag. The token needs the session:start scope.

Terminal window
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.

SymptomFix
Invite teams or people is missing in Manage accessThe package inherits access from its repository. Untick Inherit access from source repository in Package settings.
Add Tag shows No tags availableMost 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 PreloadingCheck, 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 404The tag is not available in that region yet. Wait for Ready, or for GET /tag to return {"exists":true}.
A session runs an old buildYou pushed an existing tag again. Publish a new, unique tag.

Open the webhook in GitHub and check Recent Deliveries. Each delivery shows Gameye’s response code and body.

ResponseMeaning
204The tag is registered and nodes start downloading it.
200Nothing 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 repositoryNo GHCR application in your organization has this exact Repository URL. Check spelling and lowercase. A later push registers tags once the application exists.
400The body is not JSON. Set Content type to application/json. The ping still succeeds with the wrong content type, so check a package delivery.
401The 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 pushWait 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.