|
| 1 | +# Creating a Custom Egg |
| 2 | + |
| 3 | +## Create New Egg |
| 4 | + |
| 5 | +Navigate to the Admin Panel and click the eggs option in the sidebar then select `New Egg` button. |
| 6 | + |
| 7 | +You will be taken to a new egg configuration page which is where most of the configuration happens. |
| 8 | + |
| 9 | +### Name |
| 10 | +This is the name of your egg. |
| 11 | + |
| 12 | +### Author |
| 13 | +This is the original creator's email. |
| 14 | + |
| 15 | +### Description |
| 16 | +This is the description of your egg and what is needed to run it. |
| 17 | + |
| 18 | +### Startup Commands |
| 19 | +Here you can assign multiple startup commands to run your server. |
| 20 | +Example below: |
| 21 | +```bash |
| 22 | +java -Xms128M -XX:MaxRAMPercentage=95.0 -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}} |
| 23 | +``` |
| 24 | +### File Denylist |
| 25 | +Here you can specify a list of files that the end user is not allowed to edit. |
| 26 | + |
| 27 | +### Features (Egg Features) |
| 28 | +Egg features can do different things when specified. Example below: |
| 29 | +| Tag | Description | |
| 30 | +| ------------------------- | ------------------------------------------- | |
| 31 | +| eula | Displays a popup for accepting the EULA | |
| 32 | +| java_version | Specifies the Java version | |
| 33 | +| pid_limit | Setting that restricts the maximum number of processes that can run within a container or on a system | |
| 34 | +| steam_disk_space | Sets a limit to enable an "out of disk space" Popup if the host system is out of disk space | |
| 35 | + |
| 36 | +### Tags |
| 37 | +The tag system allows you to group eggs together by type. This is the successor to nests. |
| 38 | + |
| 39 | +### Update URL |
| 40 | +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. |
| 41 | + |
| 42 | +### Docker Images (Yolks) |
| 43 | +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](https://github.com/pelican-eggs/yolks). They have two fields that need to be defined: the name of the image and the URL where the image is hosted. |
| 44 | + |
| 45 | +:::info |
| 46 | +Docker images must be specifically designed to work with Pelican Panel. You should read more about that in our Creating a Yolk guide. |
| 47 | +::: |
| 48 | + |
| 49 | +## Configure Process Management |
| 50 | + |
| 51 | +This is perhaps the most important step in egg configuration, as this tells Wings how to run everything. |
| 52 | + |
| 53 | +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. |
| 54 | + |
| 55 | +### Stop Command |
| 56 | + |
| 57 | +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. |
| 58 | + |
| 59 | +### Log Storage (Coming Soon) |
| 60 | + |
| 61 | +Logs are completely handled by Wings and use Docker logs to output the complete output from the server. This can be set like below: |
| 62 | + |
| 63 | +```json |
| 64 | +{} |
| 65 | +``` |
| 66 | + |
| 67 | +### Configuration Files |
| 68 | + |
| 69 | +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. |
| 70 | + |
| 71 | +```json |
| 72 | +{ |
| 73 | + "server.properties": { |
| 74 | + "parser": "properties", |
| 75 | + "find": { |
| 76 | + "server-ip": "0.0.0.0", |
| 77 | + "enable-query": "true", |
| 78 | + "server-port": "{{server.allocations.default.port}}", |
| 79 | + "query.port": "{{server.allocations.default.port}}" |
| 80 | + } |
| 81 | + } |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +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: |
| 86 | + |
| 87 | +- `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. |
| 88 | +- `yaml` (supports `*` notation) |
| 89 | +- `properties` |
| 90 | +- `ini` |
| 91 | +- `json` (supports `*` notation) |
| 92 | +- `xml` (supports `*` notation) |
| 93 | + |
| 94 | +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.** |
| 95 | + |
| 96 | +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. |
| 97 | + |
| 98 | +```json |
| 99 | +{ |
| 100 | + "config.yml": { |
| 101 | + "parser": "yaml", |
| 102 | + "find": { |
| 103 | + "listeners[0].query_enabled": true, |
| 104 | + "listeners[0].query_port": "{{server.allocations.default.port}}", |
| 105 | + "listeners[0].host": "0.0.0.0:{{server.allocations.default.port}}", |
| 106 | + "servers.*.address": { |
| 107 | + "127.0.0.1": "{{config.docker.interface}}", |
| 108 | + "localhost": "{{config.docker.interface}}" |
| 109 | + } |
| 110 | + } |
| 111 | + } |
| 112 | +} |
| 113 | +``` |
| 114 | + |
| 115 | +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. |
| 116 | + |
| 117 | +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}}`. |
| 118 | + |
| 119 | +### Start Configuration |
| 120 | + |
| 121 | +The last block to configure is the `Start Configuration` for servers running using this egg. |
| 122 | + |
| 123 | +```json |
| 124 | +{ |
| 125 | + "done": ")! For help, type " |
| 126 | +} |
| 127 | +``` |
| 128 | + |
| 129 | +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`. |
| 130 | + |
| 131 | +That concludes basic egg configuration. |
| 132 | + |
| 133 | +## Copy Settings From |
| 134 | + |
| 135 | +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. |
| 136 | + |
| 137 | +In the panel, we use this to copy settings that remain the same between similar eggs, such as many of the Minecraft eggs. |
| 138 | + |
| 139 | +## Egg Variables |
| 140 | + |
| 141 | +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. |
| 142 | + |
| 143 | +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. |
| 144 | + |
| 145 | +### How to Reference Egg Variables |
| 146 | + |
| 147 | +Egg variables can be referenced in three different ways depending on the context: |
| 148 | + |
| 149 | +**In Startup Commands:** |
| 150 | +```bash |
| 151 | +java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}} |
| 152 | +``` |
| 153 | + |
| 154 | +**In Configuration File Parser (find/replace blocks):** |
| 155 | +```json |
| 156 | +{ |
| 157 | + "server.properties": { |
| 158 | + "parser": "properties", |
| 159 | + "find": { |
| 160 | + "server-port": "{{server.allocations.default.port}}", |
| 161 | + "max-players": "{{server.environment.MAX_PLAYERS}}", |
| 162 | + "server-name": "{{server.environment.SERVER_NAME}}" |
| 163 | + } |
| 164 | + } |
| 165 | +} |
| 166 | +``` |
| 167 | + |
| 168 | +:::note |
| 169 | +Variable syntax depends on the context: |
| 170 | +**Startup commands**: Use `{{VAR_NAME}}` (e.g., `{{SERVER_MEMORY}}`) |
| 171 | +**Configuration file parsers**: Use `{{server.environment.VAR_NAME}}` or `{{server.allocations.default.port}}` |
| 172 | +**Install scripts and shell environments**: Use `${VAR_NAME}` |
| 173 | +::: |
| 174 | + |
| 175 | +**In Install Scripts:** |
| 176 | +```bash |
| 177 | +#!/bin/bash |
| 178 | +cd /mnt/server |
| 179 | + |
| 180 | +echo "Downloading Minecraft version ${MINECRAFT_VERSION}..." |
| 181 | +curl -o server.jar https://example.com/minecraft-${MINECRAFT_VERSION}.jar |
| 182 | + |
| 183 | +echo "Setting default config from ${CONFIG_URL}" |
| 184 | +``` |
| 185 | + |
| 186 | +The next section is `Permissions`, which is a dropdown with two options: `Users Can View` and `Users Can Edit`. |
| 187 | + |
| 188 | +- `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. |
| 189 | +- `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. |
| 190 | + |
| 191 | +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. |
| 192 | + |
| 193 | +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](https://laravel.com/docs/validation#available-validation-rules). 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`. |
| 194 | + |
| 195 | +They will then be visible when managing the startup for a server in both the Admin CP and on the Front-End. |
| 196 | + |
| 197 | +## List of Default Variables |
| 198 | + |
| 199 | +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. |
| 200 | + |
| 201 | +| Variable | Description | Example | |
| 202 | +| ------------------------- | ------------------------------------------- | -------------------------------------------------------------- | |
| 203 | +| TZ | Time Zone | `Etc/UTC` | |
| 204 | +| STARTUP | Startup command of the egg | `java -Xms128M -Xmx{{SERVER_MEMORY}}M -jar {{SERVER_JARFILE}}` | |
| 205 | +| SERVER_MEMORY | Memory available for the server in MB | `512` | |
| 206 | +| SERVER_IP | Default IP of the server | `127.0.0.1` | |
| 207 | +| SERVER_PORT | Primary Server Port | `27015` | |
| 208 | +| P_SERVER_LOCATION | Location of the server | `Example City` | |
| 209 | +| P_SERVER_UUID | UUID of the server | `539fdca8-4a08-4551-a8d2-8ee5475b50d9` | |
| 210 | +| P_SERVER_ALLOCATION_LIMIT | Limit of allocations allowed for the server | `0` | |
| 211 | + |
| 212 | +## Install Script |
| 213 | + |
| 214 | +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. |
| 215 | + |
| 216 | +### Install Script Configuration |
| 217 | + |
| 218 | +When configuring the install script, you'll need to specify: |
| 219 | + |
| 220 | +- **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.). |
| 221 | +- **Install Script**: The actual bash script that performs the installation steps. |
| 222 | +- **Script Entry**: This dropdown specifies which shell interpreter to use for running your install script: |
| 223 | + - `bash` - Use for Debian/Ubuntu-based install images |
| 224 | + - `ash` - Use for Alpine Linux-based install images |
| 225 | + |
| 226 | +### Example Install Script |
| 227 | + |
| 228 | +Here's an example of a typical install script for a Minecraft server: |
| 229 | + |
| 230 | +```bash |
| 231 | +#!/bin/bash |
| 232 | +# Minecraft Server Installation Script |
| 233 | + |
| 234 | +cd /mnt/server |
| 235 | + |
| 236 | +# Download the server jar |
| 237 | +echo "Downloading server jar..." |
| 238 | +curl -o server.jar https://example.com/minecraft-server.jar |
| 239 | + |
| 240 | +echo "Installation complete!" |
| 241 | +``` |
| 242 | + |
| 243 | +### Install Script Variables |
| 244 | + |
| 245 | +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: |
| 246 | + |
| 247 | +```bash |
| 248 | +#!/bin/bash |
| 249 | +cd /mnt/server |
| 250 | + |
| 251 | +# Download specific version using egg variable |
| 252 | +echo "Downloading Minecraft version ${MINECRAFT_VERSION}..." |
| 253 | +curl -o server.jar https://example.com/minecraft-${MINECRAFT_VERSION}.jar |
| 254 | + |
| 255 | +# Use server memory variable |
| 256 | +echo "Server will run with ${SERVER_MEMORY}MB of RAM" |
| 257 | +``` |
| 258 | + |
| 259 | +### Install Container Path |
| 260 | + |
| 261 | +:::warning |
| 262 | +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. |
| 263 | +::: |
| 264 | + |
| 265 | +### Common Install Script Patterns |
| 266 | + |
| 267 | +**Downloading from a URL:** |
| 268 | +```bash |
| 269 | +curl -sSL -o server.jar https://example.com/server.jar |
| 270 | +``` |
| 271 | + |
| 272 | +**Cloning from Git:** |
| 273 | +```bash |
| 274 | +git clone https://github.com/example/repo.git . |
| 275 | +``` |
| 276 | + |
| 277 | +**Extracting Archives:** |
| 278 | +```bash |
| 279 | +curl -sSL https://example.com/archive.tar.gz | tar -xzv |
| 280 | +``` |
| 281 | + |
| 282 | +**Building from Source:** |
| 283 | +```bash |
| 284 | +npm install |
| 285 | +npm run build |
| 286 | +``` |
| 287 | + |
| 288 | +### Install Script Exit Codes |
| 289 | + |
| 290 | +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. |
| 291 | + |
| 292 | +```bash |
| 293 | +# Check if download was successful |
| 294 | +if [ ! -f "server.jar" ]; then |
| 295 | + echo "Failed to download server.jar" |
| 296 | + exit 1 |
| 297 | +fi |
| 298 | + |
| 299 | +echo "Installation successful" |
| 300 | +exit 0 |
| 301 | +``` |
0 commit comments