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:
| Tag | Description |
|---|---|
| eula | Displays a popup for accepting the EULA |
| java_version | Specifies the Java version |
| pid_limit | Setting that restricts the maximum number of processes that can run within a container or on a system |
| steam_disk_space | Sets 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.
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)propertiesinijson(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}}"
}
}
}
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 theirserver.jarfile 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.
| Variable | Description | Example |
|---|---|---|
| TZ | Time Zone | Etc/UTC |
| STARTUP | Startup command of the egg | java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}} |
| SERVER_MEMORY | Memory available for the server in MB | 512 |
| SERVER_IP | Default IP of the server | 127.0.0.1 |
| SERVER_PORT | Primary Server Port | 27015 |
| P_SERVER_LOCATION | Location of the server | Example City |
| P_SERVER_UUID | UUID of the server | 539fdca8-4a08-4551-a8d2-8ee5475b50d9 |
| P_SERVER_ALLOCATION_LIMIT | Limit of allocations allowed for the server | 0 |
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 imagesash- 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
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