Community GuidesEgg Creation

Creating a Custom Image

Build your own Docker image for an egg, when the official images do not fit.

Introduction

If none of the official images has what your server needs, build your own. This guide builds a small Debian-based image the same way the official images are built.

You need Docker on the build machine, either a node or your own computer.

How Wings Runs an Image

The image must work with how Wings runs containers:

  • The server's files are at /home/container. Wings mounts the server's directory there. Set the image's working directory to /home/container.
  • The server does not run as root. Wings runs the container as its pterodactyl system user, whose user ID differs between nodes. Do not rely on root, or on a specific user name or ID.
  • The startup command is in the STARTUP variable. Wings does not run the command itself. It passes it in the STARTUP environment variable, with the egg's variables still written as {{NAME}}. The entrypoint must expand and run it.
  • The stop command ^C goes to the main process. If the entrypoint starts the server with exec, the server receives the signal and can shut down cleanly.

The Dockerfile

Create a directory for the image containing a Dockerfile:

Dockerfile
FROM --platform=$TARGETOS/$TARGETARCH debian:bookworm-slim

RUN apt-get update \
    && apt-get install -y --no-install-recommends ca-certificates curl iproute2 tzdata \
    && rm -rf /var/lib/apt/lists/* \
    && useradd -m -d /home/container container

USER container
ENV USER=container HOME=/home/container
WORKDIR /home/container

COPY --chmod=755 ./entrypoint.sh /entrypoint.sh
CMD ["/bin/bash", "/entrypoint.sh"]

Install everything the server needs to run in the RUN step. The container user and the USER and HOME variables give programs a normal home directory.

The Entrypoint

Next to the Dockerfile, create entrypoint.sh:

entrypoint.sh
#!/bin/bash
cd /home/container || exit 1

# Use UTC unless Wings set a time zone.
export TZ=${TZ:-UTC}

# The container's own IP address, for servers that need to know it.
INTERNAL_IP=$(ip route get 1 | awk '{for (i = 1; i < NF; i++) if ($i == "src") { print $(i + 1); exit }}')
export INTERNAL_IP

# Turn {{NAME}} into ${NAME}, then fill in the values.
PARSED=$(echo "${STARTUP}" | sed -e 's/{{/${/g' -e 's/}}/}/g' | eval echo "$(cat -)")

# Show the command in the console, then replace this script with it.
printf "\033[1m\033[33mcontainer@pterodactyl~ \033[0m%s\n" "$PARSED"
exec env ${PARSED}

Like the official images, the last line runs the command directly, not through a shell, so && and | do not work in the startup command. If an egg needs them, change the last line to exec bash -c "${PARSED}".

Building the Image

Build and tag the image. To use it on other nodes, push it to a registry such as the GitHub Container Registry:

docker build -t ghcr.io/your-name/my-image:latest .
docker push ghcr.io/your-name/my-image:latest

Then add ghcr.io/your-name/my-image:latest to the egg's Docker Images.

To test on one node without a registry, build the image on that node and prefix its name with ~, such as ~my-image:latest. Wings then uses the local image instead of pulling it. See Local Images.

Edit on GitHub

Last updated on

On this page