Skip to main content

Creating a Custom Egg

Create New Egg

Navigate to the Admin Panel and click the eggs option in the sidebar then select New Egg button.

You will be taken to a new egg configuration page which is where most of the configuration happens.

Name

This is the name of your egg.

Author

This is the original creator's email.

Description

This is the description of your egg and what is needed to run it.

Startup Commands

Here you can assign multiple startup commands to run your server. Example below:

java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}

File Denylist

Here you can specify a list of files that the end user is not allowed to edit.

Features (Egg Features)

Egg features can do different things when specified. Example below:

TagDescription
eulaDisplays a popup for accepting the EULA
java_versionSpecifies the Java version
pid_limitSetting that restricts the maximum number of processes that can run within a container or on a system
steam_disk_spaceSets a limit to enable an "out of disk space" Popup if the host system is out of disk space

Tags

The tag system allows you to group eggs together by type. This is the successor to nests.

Update URL

This can be specified so that users who use your egg can update the egg without having to look around for where the egg is hosted.

Docker Images (Yolks)

Every egg has a Docker image or Yolk as they are referred to. Images can be custom-made, and you can learn how to create them in the "Creating a Custom Yolk" guide or you can use the pre-made ones found here Yolk Repo. They have two fields that need to be defined: the name of the image and the URL where the image is hosted.

info

Docker images must be specifically designed to work with Pelican Panel. You should read more about that in our Creating a Yolk guide.

Configure Process Management

This is perhaps the most important step in egg configuration, as this tells Wings how to run everything.

The first field you'll encounter is Copy Settings From. The default selection is None. That is expected, and okay. This dropdown is discussed at the end of this article.

Stop Command

Next, you'll encounter Stop Command and, as the name implies, this should be the command used to safely stop the server. For some games, this is stop or end. Certain programs and games don't have a specified stop command, so you can enter ^C, ^SIGINT, ^SIGABRT, ^SIGTERM or ^SIGKILL to have Wings end the process.

Log Storage (Coming Soon)

Logs are completely handled by Wings and use Docker logs to output the complete output from the server. This can be set like below:

{}

Configuration Files

The next block is one of the most complex blocks, the Configuration Files descriptor. Wings will process this block prior to booting the server to ensure all of the required settings are defined and set correctly.

{
"server.properties": {
"parser": "properties",
"find": {
"server-ip": "0.0.0.0",
"enable-query": "true",
"server-port": "{{server.allocations.default.port}}",
"query.port": "{{server.allocations.default.port}}"
}
}
}

In this example, we are telling Wings to read server.properties in /home/container. Within this block, we define a parser, in this case properties but the following are valid parsers:

  • file — This parser goes based on matching the beginning of lines, and not a specific property like the other five. Avoid using this parser if possible.
  • yaml (supports * notation)
  • properties
  • ini
  • json (supports * notation)
  • xml (supports * notation)

Once you have defined a parser, we then define a find block which tells Wings what specific elements to find and replace. In this example, we have provided four separate items within the server.properties file that we want to find and replace to the assigned values. You can use either an exact value, or define a specific server setting from the server configuration. In this case, we're assigning the default server port to be used as the server.port and query.port. These placeholders are case sensitive, and should have no spaces in them.

You can have multiple files listed here, Wings will process them in parallel before starting the server. When using yaml or json you can use more advanced searching for elements.

{
"config.yml": {
"parser": "yaml",
"find": {
"listeners[0].query_enabled": true,
"listeners[0].query_port": "{{server.allocations.default.port}}",
"listeners[0].host": "0.0.0.0:{{server.allocations.default.port}}",
"servers.*.address": {
"127.0.0.1": "{{config.docker.interface}}",
"localhost": "{{config.docker.interface}}"
}
}
}
}

In this example, we are parsing config.yml using the yaml parser. The first three find items are simply assigning ports and IPs for the first listener block. The last one, servers.*.address uses wildcard matching to match any items within the servers block, and then finding each address block for those items.

An advanced feature of this file configuration is the ability to define multiple find and replace statements for a single matching line. In this case, we are looking for either 127.0.0.1 or localhost and replacing them with the docker interface defined in the configuration file using {{config.docker.interface}}.

Start Configuration

The last block to configure is the Start Configuration for servers running using this egg.

{
"done": ")! For help, type "
}

In the example block above, we define done as the entire line, or part of a line that indicates a server is done starting, and is ready for players to join. When Wings sees this output, it will mark the server as RUNNING rather than STARTING.

That concludes basic egg configuration.

Copy Settings From

As mentioned above, there is a unique Copy Settings From dropdown when adding a new egg. This is used in two different places, there is one for the Process Management and one for the Install Script. This gives you the ability to, as the name suggests, copy settings defined above from a different egg.

In the panel, we use this to copy settings that remain the same between similar eggs, such as many of the Minecraft eggs.

Egg Variables

One of the great parts of Egg Variables is the ability to define specific variables that users and/or admins can control to tweak different settings without letting users modify the startup command. To create new variables, or edit existing ones, visit the egg you created, and click the Variables tab at the top of the page. Let's take a look at an example variable that we can create.

The name and description are rather self-explanatory, so I'll skip down to the Environment Variable box. This should be an alphanumeric name with underscores, and should be uppercase. This will be the name of the environment variable which can be accessed in the startup command as {{WOOZLE_WOO}}, within file modifications as {{server.environment.WOOZLE_WOO}}, or just ${WOOZLE_WOO} in any shell scripts (it is passed through in the environment). We also define a default value for this environment variable in this example, but it is not required to do so.

How to Reference Egg Variables

Egg variables can be referenced in three different ways depending on the context:

In Startup Commands:

java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}

In Configuration File Parser (find/replace blocks):

{
"server.properties": {
"parser": "properties",
"find": {
"server-port": "{{server.allocations.default.port}}",
"max-players": "{{server.environment.MAX_PLAYERS}}",
"server-name": "{{server.environment.SERVER_NAME}}"
}
}
}
note

Variable syntax depends on the context: Startup commands: Use {{VAR_NAME}} (e.g., {{SERVER_MEMORY}}) Configuration file parsers: Use {{server.environment.VAR_NAME}} or {{server.allocations.default.port}} Install scripts and shell environments: Use ${VAR_NAME}

In Install Scripts:

#!/bin/bash
cd /mnt/server

echo "Downloading Minecraft version ${MINECRAFT_VERSION}..."
curl -o server.jar https://example.com/minecraft-${MINECRAFT_VERSION}.jar

echo "Setting default config from ${CONFIG_URL}"

The next section is Permissions, which is a dropdown with two options: Users Can View and Users Can Edit.

  • Users Can View — allows a user to view the field on the front-end, as well as the assigned value of that variable. They will be able to see it replaced in their startup command.
  • Users Can Edit — allows a user to edit the value of the variable, for example the name of their server.jar file if running Minecraft.

You should use caution here, even if you assign neither of the permissions it does not mean that the value will be hidden. Crafty users will still be able to get the environment on their server. In most cases this is simply hiding it from the user, and then used within the Dockerfile to perform actions, thus it is not important for the user to see.

Finally, you will need to define some input rules to validate the value against. In this example, we use required|string|between:1,10, which means the field is required, must be a string, and must be between 1 and 10 characters in length. You can find all of the available validation rules in the Laravel validation documentation. You can also use regex-based validation by using the regex: rule flag. For example, required|regex:/^([\w\d._-]+)(\.jar)$/ will require the field, and will match the regex as any letters or numbers (\w\d) including underscore (_), periods (.), and dashes (-) ending in .jar.

They will then be visible when managing the startup for a server in both the Admin CP and on the Front-End.

List of Default Variables

The default variables are always accessible to all eggs and don't have to be created separately. They can be used in the egg startup, install script, or the configuration file parser. Below are an example of a few.

VariableDescriptionExample
TZTime ZoneEtc/UTC
STARTUPStartup command of the eggjava -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}
SERVER_MEMORYMemory available for the server in MB512
SERVER_IPDefault IP of the server127.0.0.1
SERVER_PORTPrimary Server Port27015
P_SERVER_LOCATIONLocation of the serverExample City
P_SERVER_UUIDUUID of the server539fdca8-4a08-4551-a8d2-8ee5475b50d9
P_SERVER_ALLOCATION_LIMITLimit of allocations allowed for the server0

Install Script

The install script is a crucial component of your egg that defines how the server files are downloaded, installed, and configured before the server can run for the first time. This script runs in a separate container during the installation process.

Install Script Configuration

When configuring the install script, you'll need to specify:

  • Script Container: The Docker image that will be used to run the installation. This is often different from the runtime image and typically includes tools needed for downloading and extracting files (like curl, wget, git, unzip, etc.).
  • Install Script: The actual bash script that performs the installation steps.
  • Script Entry: This dropdown specifies which shell interpreter to use for running your install script:
    • bash - Use for Debian/Ubuntu-based install images
    • ash - Use for Alpine Linux-based install images

Example Install Script

Here's an example of a typical install script for a Minecraft server:

#!/bin/bash
# Minecraft Server Installation Script

cd /mnt/server

# Download the server jar
echo "Downloading server jar..."
curl -o server.jar https://example.com/minecraft-server.jar

echo "Installation complete!"

Install Script Variables

Install scripts have access to all egg variables as well as some special installation-specific variables. You can reference these using the ${VARIABLE_NAME} syntax:

#!/bin/bash
cd /mnt/server

# Download specific version using egg variable
echo "Downloading Minecraft version ${MINECRAFT_VERSION}..."
curl -o server.jar https://example.com/minecraft-${MINECRAFT_VERSION}.jar

# Use server memory variable
echo "Server will run with ${SERVER_MEMORY}MB of RAM"

Install Container Path

warning

Install scripts run in /mnt/server instead of /home/container. All files you download or create during installation should be placed in /mnt/server, which will be mapped to /home/container when the server runs.

Common Install Script Patterns

Downloading from a URL:

curl -sSL -o server.jar https://example.com/server.jar

Cloning from Git:

git clone https://github.com/example/repo.git .

Extracting Archives:

curl -sSL https://example.com/archive.tar.gz | tar -xzv

Building from Source:

npm install
npm run build

Install Script Exit Codes

Your install script should exit with code 0 on success. Any non-zero exit code will be treated as a failure and the installation will be marked as failed.

# Check if download was successful
if [ ! -f "server.jar" ]; then
echo "Failed to download server.jar"
exit 1
fi

echo "Installation successful"
exit 0