Hytale is a block-based sandbox game that blends creative building with RPG adventure. Explore procedurally generated worlds filled with unique biomes, dungeons, and creatures. Build anything from simple homes to grand castles, craft items, and battle monsters. Create custom content with built-in modding tools, host your own servers, and shape the experience with plugins and mods. Play solo or with friends in a world designed for both creation and exploration.
Default startup command from Pterodactyl egg
#!/bin/bash
################################################################################
# egg-hytale Entry Script
#
# This script handles:
# - Hytale server download and updates
# - Authentication with Hytale services
# - Session token generation
# - Launching the main server startup script
#
# DO NOT EDIT THIS FILE - it is managed by the Docker image.
# To customize server settings, use the egg configuration variables in your
# Pelican or Pterodactyl panel instead.
################################################################################
DOWNLOAD_URL="https://downloader.hytale.com/hytale-downloader.zip"
DOWNLOAD_FILE="hytale-downloader.zip"
DOWNLOADER="./hytale-downloader-linux-amd64"
AUTH_CACHE_FILE=".hytale-auth-tokens.json"
# Function to extract downloaded server files
extract_server_files() {
echo "Extracting server files..."
SERVER_ZIP="server.zip"
if [ -f "$SERVER_ZIP" ]; then
echo "Found server archive: $SERVER_ZIP"
# Extract to current directory
unzip -o "$SERVER_ZIP"
if [ $? -ne 0 ]; then
echo "Error: Failed to extract $SERVER_ZIP"
exit 1
fi
echo "Extraction completed successfully."
# Move contents from Server folder to current directory
if [ -d "Server" ]; then
echo "Moving server files from Server directory..."
mv Server/* .
rmdir Server
echo "✓ Server files moved to root directory."
fi
# Clean up the zip file
echo "Cleaning up archive file..."
rm "$SERVER_ZIP"
echo "✓ Archive removed."
else
echo "Error: Server archive not found at $SERVER_ZIP"
exit 1
fi
}
# Function to check if cached tokens exist
check_cached_tokens() {
if [ -f "$AUTH_CACHE_FILE" ]; then
# Check if jq is available
if ! command -v jq &> /dev/null; then
echo "Warning: jq not found, cannot use cached tokens"
return 1
fi
# Validate JSON format
if ! jq empty "$AUTH_CACHE_FILE" 2>/dev/null; then
echo "Warning: Invalid cached token file, removing..."
rm "$AUTH_CACHE_FILE"
return 1
fi
echo "✓ Found cached authentication tokens"
return 0
fi
return 1
}
# Function to load cached tokens (refresh_token + profile_uuid only)
load_cached_tokens() {
REFRESH_TOKEN=$(jq -r '.refresh_token' "$AUTH_CACHE_FILE")
PROFILE_UUID=$(jq -r '.profile_uuid' "$AUTH_CACHE_FILE")
# Validate required tokens are present
if [ -z "$REFRESH_TOKEN" ] || [ "$REFRESH_TOKEN" = "null" ] || \
[ -z "$PROFILE_UUID" ] || [ "$PROFILE_UUID" = "null" ]; then
echo "Error: Incomplete cached tokens, re-authenticating..."
rm "$AUTH_CACHE_FILE"
return 1
fi
echo "✓ Loaded cached refresh token + profile UUID"
return 0
}
# Function to refresh access token using cached refresh token
refresh_access_token() {
echo "Refreshing access token..."
TOKEN_RESPONSE=$(curl -s -X POST "https://oauth.accounts.hytale.com/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=hytale-server" \
-d "grant_type=refresh_token" \
-d "refresh_token=$REFRESH_TOKEN")
ERROR=$(echo "$TOKEN_RESPONSE" | jq -r '.error // empty')
if [ -n "$ERROR" ]; then
echo "Error: Failed to refresh access token: $ERROR"
return 1
fi
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')
NEW_REFRESH_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.refresh_token // empty')
if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
echo "Error: No access token in refresh response"
return 1
fi
# Update refresh token if a new one was provided
if [ -n "$NEW_REFRESH_TOKEN" ] && [ "$NEW_REFRESH_TOKEN" != "null" ]; then
REFRESH_TOKEN="$NEW_REFRESH_TOKEN"
fi
echo "✓ Access token refreshed"
return 0
}
# Function to create a new game session
create_game_session() {
echo "Creating game server session..."
SESSION_RESPONSE=$(curl -s -X POST "https://sessions.hytale.com/game-session/new" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"uuid\": \"${PROFILE_UUID}\"}")
# Validate JSON response
if ! echo "$SESSION_RESPONSE" | jq empty 2>/dev/null; then
echo "Error: Invalid JSON response from game session creation"
echo "Response: $SESSION_RESPONSE"
return 1
fi
# Extract session and identity tokens
SESSION_TOKEN=$(echo "$SESSION_RESPONSE" | jq -r '.sessionToken')
IDENTITY_TOKEN=$(echo "$SESSION_RESPONSE" | jq -r '.identityToken')
if [ -z "$SESSION_TOKEN" ] || [ "$SESSION_TOKEN" = "null" ]; then
echo "Error: Failed to create game server session"
echo "Response: $SESSION_RESPONSE"
return 1
fi
echo "✓ Game server session created successfully!"
return 0
}
# Function to save authentication tokens (refresh_token + profile_uuid only)
save_auth_tokens() {
cat > "$AUTH_CACHE_FILE" << EOF
{
"refresh_token": "$REFRESH_TOKEN",
"profile_uuid": "$PROFILE_UUID",
"timestamp": $(date +%s)
}
EOF
echo "✓ Refresh token cached for future use"
}
# Function to perform full authentication
perform_authentication() {
echo "Obtaining authentication tokens..."
# Step 1: Request device code
AUTH_RESPONSE=$(curl -s -X POST "https://oauth.accounts.hytale.com/oauth2/device/auth" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=hytale-server" \
-d "scope=openid offline auth:server")
# Extract device_code and verification_uri_complete using jq
DEVICE_CODE=$(echo "$AUTH_RESPONSE" | jq -r '.device_code')
VERIFICATION_URI=$(echo "$AUTH_RESPONSE" | jq -r '.verification_uri_complete')
POLL_INTERVAL=$(echo "$AUTH_RESPONSE" | jq -r '.interval')
# Display authentication banner
echo ""
echo "╔═════════════════════════════════════════════════════════════════════════════╗"
echo "║ HYTALE SERVER AUTHENTICATION REQUIRED ║"
echo "╠═════════════════════════════════════════════════════════════════════════════╣"
echo "║ ║"
echo "║ Please authenticate the server by visiting the following URL: ║"
echo "║ ║"
echo "║ $VERIFICATION_URI ║"
echo "║ ║"
echo "║ 1. Click the link above or copy it to your browser ║"
echo "║ 2. Sign in with your Hytale account ║"
echo "║ 3. Authorize the server ║"
echo "║ ║"
echo "║ Waiting for authentication... ║"
echo "║ ║"
echo "╚═════════════════════════════════════════════════════════════════════════════╝"
echo ""
# Step 2: Poll for access token
ACCESS_TOKEN=""
while [ -z "$ACCESS_TOKEN" ]; do
sleep $POLL_INTERVAL
TOKEN_RESPONSE=$(curl -s -X POST "https://oauth.accounts.hytale.com/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=hytale-server" \
-d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
-d "device_code=$DEVICE_CODE")
# Check if we got an error
ERROR=$(echo "$TOKEN_RESPONSE" | jq -r '.error // empty')
if [ "$ERROR" = "authorization_pending" ]; then
echo "Still waiting for authentication..."
continue
elif [ -n "$ERROR" ]; then
echo "Authentication error: $ERROR"
exit 1
else
# Successfully authenticated
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')
REFRESH_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.refresh_token')
echo ""
echo "✓ Authentication successful!"
echo ""
fi
done
# Fetch available game profiles
echo "Fetching game profiles..."
PROFILES_RESPONSE=$(curl -s -X GET "https://account-data.hytale.com/my-account/get-profiles" \
-H "Authorization: Bearer $ACCESS_TOKEN")
# Check if profiles list is empty
PROFILES_COUNT=$(echo "$PROFILES_RESPONSE" | jq '.profiles | length')
if [ "$PROFILES_COUNT" -eq 0 ]; then
echo "Error: No game profiles found. You need to purchase Hytale to run a server."
exit 1
fi
# Select profile based on GAME_PROFILE variable
if [ -n "$GAME_PROFILE" ]; then
# User specified a profile username, find matching UUID
echo "Looking for profile: $GAME_PROFILE"
PROFILE_UUID=$(echo "$PROFILES_RESPONSE" | jq -r ".profiles[] | select(.username == \"$GAME_PROFILE\") | .uuid")
if [ -z "$PROFILE_UUID" ] || [ "$PROFILE_UUID" = "null" ]; then
echo "Error: Profile '$GAME_PROFILE' not found."
echo "Available profiles:"
echo "$PROFILES_RESPONSE" | jq -r '.profiles[] | " - \(.username)"'
exit 1
fi
echo "✓ Using profile: $GAME_PROFILE (UUID: $PROFILE_UUID)"
else
# Use first profile from the list
PROFILE_UUID=$(echo "$PROFILES_RESPONSE" | jq -r '.profiles[0].uuid')
PROFILE_USERNAME=$(echo "$PROFILES_RESPONSE" | jq -r '.profiles[0].username')
echo "✓ Using default profile: $PROFILE_USERNAME (UUID: $PROFILE_UUID)"
fi
echo ""
# Save refresh token + profile for future use
save_auth_tokens
# Create game server session
if ! create_game_session; then
exit 1
fi
echo ""
}
chmod 755 start.sh
# Check if the downloader exists
if [ ! -f "$DOWNLOADER" ]; then
echo "Error: Hytale downloader not found!"
echo "Please run the installation script first."
exit 1
fi
# Check if the downloader is executable
if [ ! -x "$DOWNLOADER" ]; then
echo "Setting executable permissions..."
chmod +x "$DOWNLOADER"
fi
INITIAL_SETUP=0
# Check if credentials file exists, if not run the updater
if [ ! -f ".hytale-downloader-credentials.json" ]; then
INITIAL_SETUP=1
echo "Credentials file not found, running initial setup..."
echo "Starting Hytale downloader..."
$DOWNLOADER -check-update
$DOWNLOADER -patchline $PATCHLINE -download-path server.zip
extract_server_files
# Save version info after initial setup
DOWNLOADER_VERSION=$($DOWNLOADER -print-version)
echo "$DOWNLOADER_VERSION" > version.txt
echo "Version info saved for later use!"
fi
# Run automatic update if enabled
if [ "${AUTOMATIC_UPDATE}" = "1" ] && [ "${INITIAL_SETUP}" = "0" ]; then
echo "Starting Hytale downloader..."
# Read local version from file
if [ -f "version.txt" ]; then
LOCAL_VERSION=$(cat version.txt)
else
echo "version.txt not found, forcing update"
LOCAL_VERSION=""
fi
# Get remote/downloader version
DOWNLOADER_VERSION=$($DOWNLOADER -print-version)
echo "Local version: $LOCAL_VERSION"
echo "Downloader version: $DOWNLOADER_VERSION"
# Compare versions
if [ "$LOCAL_VERSION" != "$DOWNLOADER_VERSION" ]; then
echo "Version mismatch, running update..."
$DOWNLOADER -check-update
$DOWNLOADER -patchline $PATCHLINE -download-path server.zip
extract_server_files
# Update version.txt after successful update
echo "$DOWNLOADER_VERSION" > version.txt
echo "Version info saved for later use!"
else
echo "Versions match, skipping update"
fi
fi
# Check if server files were downloaded correctly
if [ ! -f "HytaleServer.jar" ]; then
echo "Error: HytaleServer.jar not found!"
echo "Server files were not downloaded correctly."
exit 1
fi
# Check for cached authentication tokens
if check_cached_tokens && load_cached_tokens; then
echo "Using cached authentication..."
if refresh_access_token; then
# Update cache in case refresh token rotated
save_auth_tokens
# Create fresh game session
if ! create_game_session; then
exit 1
fi
else
# Refresh failed, need full re-auth
echo "Refresh token expired, re-authenticating..."
rm -f "$AUTH_CACHE_FILE"
perform_authentication
fi
else
# Perform full authentication if no valid cache exists
perform_authentication
fi
# Export the session tokens so they're available to start.sh
export SESSION_TOKEN
export IDENTITY_TOKEN
export PROFILE_UUID
# Now call the pterodactyl entrypoint which will execute start.sh
exec /bin/bash ./start.shAccept Early Plugins
Acknowledge that loading early plugins is unsupported and may cause stability issues
0Allow Operators
Do you wish to allow operators or not
0Asset Pack
Assets pack (.zip) that are being send to player
Assets.zipAuth Mode
Authentication mode
authenticatedAutomatic Update
Update the hytale server automaticly
1Backup Frequency
Backup interval in minutes
30Disable Sentry Crash Reporting
Important: Disable Sentry during active plugin development. Hytale use Sentry to track crashes. Disable it to avoid submitting your development errors
1Enable Backups
Enable automatic backups
0JVM Arguments
Additional Java Virtual Machine arguments for advanced configuration. Warning: Improper JVM settings can lead to poor performance, crashes, or failure to start. Only modify if you understand what these parameters do.
-XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1Leverage Ahead-Of-Time Cache
The server ships with a pre-trained AOT cache (HytaleServer.aot) that improves boot times by skipping JIT warmup. See https://openjdk.org/jeps/514
1Game Profile (username)
Specify which Hytale profile should be used for server authentication. How to find your profile username: 1. Visit https://accounts.hytale.com/ 2. Click "Game Profiles" in the left side menu 3. Copy the username of the profile you want to use 4. Paste it into this field Leave empty to use your default/first profile automatically.
(empty)Patchline
What release channel you want to use
releaseMemory overhead
The amount of RAM (in MB) kept aside for the system so the server doesn’t use everything. Java will get the rest.
0Installation script imported from Pterodactyl egg
#!/bin/bash
DOWNLOAD_URL="https://downloader.hytale.com/hytale-downloader.zip"
DOWNLOAD_FILE="hytale-downloader.zip"
apt update
apt install -y curl unzip jq
cd /mnt/server
# Downloads and extracts the Hytale downloader
echo "Starting Hytale downloader installation..."
# Download the file
echo "Downloading hytale-downloader.zip..."
curl -L -o "${DOWNLOAD_FILE}" "${DOWNLOAD_URL}"
if [ $? -ne 0 ]; then
echo "Error: Failed to download ${DOWNLOAD_URL}"
exit 1
fi
echo "Download completed...#!/bin/bash
set -e # Exit on any error
echo "Installing Hytale Query Plugins..."
# Install curl
apt-get update && apt-get install -y curl
cd /mnt/server
mkdir -p mods
# Download Nitrado:WebServer JAR from releases
echo "Downloading Nitrado:WebServer v1.0.0..."
curl -L -o /mnt/server/mods/Nitrado-WebServer.jar \
"https://github.com/nitrado/hytale-plugin-webserver/releases/download/v1.0.0/nitrado-webserver-1.0.0.jar"
# Download Nitrado:Query JAR from releases
echo "Downloading Nitrado:Qu...ghcr.io/parkervcp/yolks:java_25
hytale
Community
January 14, 2026