curl --request POST \
--url https://api.simpl.gg/v1/builds/docker-image \
--header 'Content-Type: application/json' \
--header 'api-key: <api-key>' \
--data '
{
"name": "1.2.3",
"image_url": "registry.example.com/game/server:1.2.3"
}
'{
"build_id": "3f6c2a9e-8b1d-4c5e-9a7f-2d4e6b8c0a1f",
"name": "1.2.3",
"version": 1,
"status": "processing",
"build_type": "docker_image",
"executable_path": null,
"image_url": "ghcr.io/example/game-server:1.2.3",
"error_message": null,
"base_version": null,
"created_at": "2026-09-27T12:00:00Z",
"updated_at": "2026-09-27T12:00:00Z"
}Register a Docker image build
Registers an existing Docker image as a build. The build starts in
processing and moves to ready, or to failed with an
error_message.
Simpl copies the image’s linux/amd64 version into its own registry
and builds the server image from that copy, never from where the image
sits, so a tag moved or deleted later changes nothing. An image with no
linux/amd64 version fails, naming the platforms it has.
The Simpl entrypoint is added in front of the image’s own ENTRYPOINT
and CMD, so servers from a Docker image get the same network contract
as ZIP builds: listen on SIMPL_PORT_GAME (the project’s port, UDP
7777 by default) and bind the addresses given. The image runs as its
own USER, in its own WORKDIR, with no added capabilities, and
/simpl/its_simpl.json is at the same absolute path as for ZIP builds.
Private images: send registry_username and registry_password.
A read-only token is enough. They are used once, for the copy, and then
deleted; they are never returned or shown. Stored copies are not
publicly listed.
Ports take only a name and a protocol. Fields not in the request
schema are refused with 400.
curl --request POST \
--url https://api.simpl.gg/v1/builds/docker-image \
--header 'Content-Type: application/json' \
--header 'api-key: <api-key>' \
--data '
{
"name": "1.2.3",
"image_url": "registry.example.com/game/server:1.2.3"
}
'{
"build_id": "3f6c2a9e-8b1d-4c5e-9a7f-2d4e6b8c0a1f",
"name": "1.2.3",
"version": 1,
"status": "processing",
"build_type": "docker_image",
"executable_path": null,
"image_url": "ghcr.io/example/game-server:1.2.3",
"error_message": null,
"base_version": null,
"created_at": "2026-09-27T12:00:00Z",
"updated_at": "2026-09-27T12:00:00Z"
}Authorizations
Per-project server key, prefixed sk_live_. Full access to every
endpoint. Keep it on your backend: never ship it in a game binary.
Query Parameters
Which project to act on. Required when authenticating with a developer
session (bearer_auth), which names no project of its own; ignored when
authenticating with an API key, which carries its project already.
A project belonging to another account returns PROJECT_NOT_FOUND (7001)
rather than a 403, so this cannot be used to probe for project ids.
"proj_abc123"
Body
- Option 1
- Option 2
Register an existing Docker image as a build. Only the fields below are
accepted: any other field is refused with 400 "Unknown field", which
names it.
"1.2.3"
The image to copy, as you would pass it to docker pull, without a
scheme and without credentials. It must have a linux/amd64 version
and set an ENTRYPOINT or CMD. Returned as image_url on the
build.
512"registry.example.com/game/server:1.2.3"
Another name for image_url, kept for older clients. Send one of
the two. If both are sent they must be the same, or the request is
refused with 400.
512For an image in a private registry, with registry_password. A
read-only token is enough. Used once, to copy the image, and then
deleted. Never returned. Omit both for a public image.
1 - 256The password or token for registry_username. Used once, to copy the
image, and then deleted. Never returned.
1 - 4096Ports every server started from this build exposes. Each entry takes only
a name and a protocol: there is no port number, because each server
gets its own external port numbers when it starts. They are in
network_ports, and the port the game listens on for each name is the
project's (ProjectPort).
The game port is the port named game, or the first declared port if
none is. Its number is always in SIMPL_PORT_GAME, which is what the
Simpl entrypoint's {{PORT}} and the starter scripts read.
1 - 8 elementsShow child attributes
Show child attributes
Command-line arguments for every server started from this build.
- ZIP build: baked into the image after the executable.
- docker_image build: appended to the image's own
ENTRYPOINTandCMDwhen each server starts.
For both, the Simpl entrypoint substitutes placeholders such as
{{PORT}} in them.
A server's startup_args come after these.
641024["-log"]
Response
Build registered and processing.
"build_9f2c"
Build name. Servers reference this as version_tag.
"1.2.3"
Monotonic integer version assigned per build name.
12
uploading, processing, ready, failed, deleted zip, docker_image Path to the server executable inside the ZIP. zip builds only.
"Linux/GameServer.sh"
Docker image reference. docker_image builds only.
"registry.example.com/game/server:1.2.3"
Why the build failed, when status is failed. Null otherwise.
One sentence, and the whole explanation: build output is not stored. It is on every build so that a client rendering a list of builds can say what went wrong without a second call per failed row.
"No executable found at Linux/GameServer.sh"
Which Simpl base the build's entrypoint comes from: the base a zip
build was built on, or the base a docker_image build was wrapped
with. That entrypoint runs in front of the game, so this answers
which runtime contract the build's servers get. The build is pinned
to that base: publishing a newer one never changes a build that
exists.
Null for builds that have not been built yet, for docker_image
builds registered before Docker images were wrapped, and for some
builds made before this field existed.
"2"