Skip to content

Carrier Command 2

Carrier Command 2 dedicated server (Windows binary, runs on Linux via Wine). Requires Steam ownership of CC2 to install. Uses 4 consecutive UDP ports (default 25565-25568).

Report an issue
updated
variables
17
images
1
author
88603725+norto22@users.noreply.github.com
sha256
99f5c1b60876

Readme

Carrier Command 2

Steam Description

Carrier Command 2 is a real-time strategy game in which you command an aircraft carrier and its squadrons of land, sea, and air units across an archipelago. Conquer islands, manage logistics, and battle enemy carriers in solo or multiplayer.

Authors / Contributors

Steam Ownership Required

The Carrier Command 2 dedicated server is not available for anonymous SteamCMD download. You must provide Steam credentials (STEAM_USER / STEAM_PASS) for a Steam account that owns the game.

Steam Guard 2FA: if your account has Steam Guard enabled, you must enter the current code in STEAM_AUTH when first creating the server. Steam Guard codes rotate every 30 seconds; grab a fresh one immediately before clicking install or the SteamCMD login will time out.

Throwaway account recommended: because the password field is stored in panel variables (encrypted at rest but visible to anyone with edit access on the server), consider using a dedicated alt Steam account that owns only CC2, rather than your main account.

Recommended Server Settings

RAM

Minimum 4 GB. CC2's dedicated server has been observed using 2-3 GB at idle on small worlds; larger worlds and active battles use more.

CPU

Minimum 2 vCPU cores. CC2's server is sensitive to thread synchronization under Wine; VM-virtualized cores may underperform compared to bare metal.

Storage

Minimum 5 GB. The game install is ~1.3 GB, plus SteamCMD, Wine prefix, and the Steamworks SDK redistributable files.

Server Ports

Carrier Command 2 requires 4 consecutive UDP ports starting at the primary allocation. The server listens on the base port and the next three for Steam communications.

Port Purpose
<base> (default 25565) Main game traffic
<base+1> Steam extended server info
<base+2> Reserved
<base+3> Steam server query

Allocate all four consecutive UDP ports to the server in Pterodactyl before creating it. The server will fail to register with Steam Master Server if any of the four are not open.

Variables

Variable Description Default
SERVER_NAME Display name in the server browser A Pterodactyl Hosted Server
SERVER_PASSWORD Optional join password (none)
MAX_PLAYERS Max simultaneous players (1-16) 4
SAVE_NAME Folder under saved_games/ to load on start (none)
ISLAND_COUNT Number of islands (4-32) 16
TEAM_COUNT_AI AI-controlled enemy teams (0-4) 1
TEAM_COUNT_HUMAN Human-controlled teams (1-4) 1
CARRIER_COUNT_PER_TEAM Carriers per team (1-4) 1
ISLAND_COUNT_PER_TEAM Starting islands per team (1-8) 1
BASE_DIFFICULTY Base capture difficulty (0=easy, 1=normal, 2=hard) 1
LOADOUT_TYPE Starting carrier loadout (0=standard, 1=light, 2=heavy) 0
BLUEPRINTS Blueprint unlocks (0=research required, 1=all unlocked) 0
STEAM_USER Steam username (account owning CC2)
STEAM_PASS Steam password
STEAM_AUTH Steam Guard code (first install only)
SRCDS_APPID Steam App ID (read-only) 1489630
AUTO_UPDATE Re-run SteamCMD on every server start 0

Known Limitations

The CC2 dedicated server can LOAD save games but cannot WRITE them

This is a CC2 server bug, not an egg bug. All in-game progress is lost when the server stops. The server can boot from an existing save by setting SAVE_NAME, but ongoing state is never persisted back to disk.

References:

In-game LAN/local server browser may not find the server

When connecting from the same host or LAN, the in-game browser sometimes fails to discover the server. Use carrier_command.exe +connect <ip>:<port> from a Windows shortcut, or join via the Public Servers list. Public discovery via Steam Master Server works correctly.

AUTO_UPDATE=1 adds ~50 MB of Linux Steam Runtime files per start

When AUTO_UPDATE is enabled, the runtime container logs into Steam on every server start. Steam's backend then pushes the Linux variant of the Steamworks SDK Redistributable (app 1007) into the install directory, adding steam-runtime/, libsteamwebrtc.so, and other files alongside the Windows DLLs we need.

This does not break the server — the Windows DLLs we placed during install survive, and CC2 still starts correctly. But it adds a ~330 MB download and several seconds to every restart, plus burns a Steam Guard challenge if your session has expired.

The default is AUTO_UPDATE=0 and we recommend keeping it there. If you need automatic patch updates, the trade-off is yours to make per-server.

Implementation Notes

Wine, not Proton

This egg uses the standard Pterodactyl Wine yolk (ghcr.io/ptero-eggs/yolks:wine_latest) rather than Proton. Two reasons:

  1. Steam DLL availability. CC2's dedicated_server.exe requires steamclient.dll, tier0_s.dll, and vstdlib_s.dll — Windows DLLs Geometa did not ship with the dedicated server. These are obtained anonymously from Steamworks SDK Redistributables (app 1007) at install time and placed next to dedicated_server.exe.
  2. CC2 stdout reaches the panel. Under Proton, CC2's console messages never reach Pterodactyl's stdout. Under Wine they do, enabling accurate "done" detection via the connected to Steam message and proper "running" status in the panel.

This approach matches the same recipe used by the AMP CC2 module (see CubeCoders/AMPTemplates/carrier-command2.kvp for reference).

App 1007 is installed to a temporary directory

The install script installs Steamworks SDK Redistributables to /tmp/sdk_redist, copies out the three required DLLs, then deletes the temp directory. This ensures no appmanifest_1007.acf exists in /home/container/steamapps/, which prevents the runtime image's auto-update from re-pulling app 1007 in a way that could relocate or overwrite the DLLs.

Server config is templated by inline sed

On every server start, the startup command runs 13 sed substitutions against server_config.xml to inject the current values of SERVER_PORT, SERVER_NAME, MAX_PLAYERS, and the gameplay tuning variables.

Caveat: the substitutions use % as the sed delimiter. Server names containing a literal % character will break the substitution. This is a known limitation; pick a delimiter-safe name.

Startup command

cd /home/container && sed -i -e 's%port="[^"]*"%port="'"${SERVER_PORT}"'"%' -e 's%max_players="[^"]*"%max_players="'"${MAX_PLAYERS}"'"%' -e 's%server_name="[^"]*"%server_name="'"${SERVER_NAME}"'"%' -e 's%password="[^"]*"%password="'"${SERVER_PASSWORD}"'"%' -e 's%save_name="[^"]*"%save_name="'"${SAVE_NAME}"'"%' -e 's%island_count="[^"]*"%island_count="'"${ISLAND_COUNT}"'"%' -e 's%island_count_per_team="[^"]*"%island_count_per_team="'"${ISLAND_COUNT_PER_TEAM}"'"%' -e 's%carrier_count_per_team="[^"]*"%carrier_count_per_team="'"${CARRIER_COUNT_PER_TEAM}"'"%' -e 's%team_count_ai="[^"]*"%team_count_ai="'"${TEAM_COUNT_AI}"'"%' -e 's%team_count_human="[^"]*"%team_count_human="'"${TEAM_COUNT_HUMAN}"'"%' -e 's%base_difficulty="[^"]*"%base_difficulty="'"${BASE_DIFFICULTY}"'"%' -e 's%loadout_type="[^"]*"%loadout_type="'"${LOADOUT_TYPE}"'"%' -e 's%blueprints="[^"]*"%blueprints="'"${BLUEPRINTS}"'"%' server_config.xml && WINEPREFIX=/home/container/.wine WINEARCH=win32 WINEDEBUG=-all LD_LIBRARY_PATH=/home/container/linux64:$LD_LIBRARY_PATH SteamAppId=1489630 wine /home/container/dedicated_server.exe

The parts in blue are filled in by the panel when the server starts, from the variables below.

Docker image

  • Wine ghcr.io/ptero-eggs/yolks:wine_latest

Panel features

  • steam_disk_space

1 contributor

Variables

17 settings

  • Server Name

    The display name shown to players in the server browser.

    env
    SERVER_NAME
    default
    A Pterodactyl Hosted Server
    rules
    required|string|max:128
    • viewable
    • editable
  • Server Password

    Optional password players must enter to join. Leave blank for no password.

    env
    SERVER_PASSWORD
    default
    none
    rules
    nullable|string|max:64
    • viewable
    • editable
  • Max Players

    Maximum simultaneous players (1-16).

    env
    MAX_PLAYERS
    default
    4
    rules
    required|integer|between:1,16
    • viewable
    • editable
  • Save Name

    Folder name under saved_games/ for the save the server loads on launch. Leave blank to start a fresh game. NOTE: CC2 dedicated server cannot WRITE saves — progress is lost on stop.

    env
    SAVE_NAME
    default
    none
    rules
    nullable|string|max:64
    • viewable
    • editable
  • Island Count

    Number of islands generated in the world (4-32).

    env
    ISLAND_COUNT
    default
    16
    rules
    required|integer|between:4,32
    • viewable
    • editable
  • AI Team Count

    Number of AI-controlled enemy teams (0-4).

    env
    TEAM_COUNT_AI
    default
    1
    rules
    required|integer|between:0,4
    • viewable
    • editable
  • Human Team Count

    Number of human-controlled teams (1-4).

    env
    TEAM_COUNT_HUMAN
    default
    1
    rules
    required|integer|between:1,4
    • viewable
    • editable
  • Carriers per Team

    Number of carriers each team gets (1-4).

    env
    CARRIER_COUNT_PER_TEAM
    default
    1
    rules
    required|integer|between:1,4
    • viewable
    • editable
  • Starting Islands per Team

    Number of islands each team controls at game start (1-8).

    env
    ISLAND_COUNT_PER_TEAM
    default
    1
    rules
    required|integer|between:1,8
    • viewable
    • editable
  • Base Difficulty

    Difficulty of capturing enemy bases. 0 = easy, 1 = normal, 2 = hard.

    env
    BASE_DIFFICULTY
    default
    0
    rules
    required|integer|between:0,2
    • viewable
    • editable
  • Loadout Type

    Starting carrier loadout. 0 = default, 1 = minimal, 2 = full.

    env
    LOADOUT_TYPE
    default
    0
    rules
    required|integer|in:0,1,2
    • viewable
    • editable
  • Blueprints

    Blueprint unlock state. 0 = default (research required), 1 = all unlocked.

    env
    BLUEPRINTS
    default
    0
    rules
    required|integer|in:0,1
    • viewable
    • editable
  • Steam Username

    Steam account that owns Carrier Command 2. Required — CC2 does not allow anonymous SteamCMD install.

    env
    STEAM_USER
    default
    none
    rules
    required|string
    • viewable
    • editable
  • Steam Password

    Password for the Steam account above. The password is stored encrypted but visible in cleartext to anyone with edit access to the server.

    env
    STEAM_PASS
    default
    none
    rules
    required|string
    • viewable
    • editable
  • Steam Guard Code

    Steam Guard 2FA code. Required on first install only; can be left blank after the account is trusted.

    env
    STEAM_AUTH
    default
    none
    rules
    nullable|string
    • viewable
    • editable
  • Steam App ID

    Steam App ID for Carrier Command 2.

    env
    SRCDS_APPID
    default
    1489630
    rules
    required|string|in:1489630
  • Auto Update

    Re-run SteamCMD app_update on every server start. 0 = off, 1 = on.

    env
    AUTO_UPDATE
    default
    0
    rules
    required|boolean
    • viewable
    • editable
Installation script 91 lines container ghcr.io/ptero-eggs/installers:debian entrypoint bash show
#!/bin/bash
# Carrier Command 2 dedicated server install (Wine + AMP recipe)
# Server Files: /mnt/server
# Install image: ghcr.io/ptero-eggs/installers:debian
#
# Required env vars: STEAM_USER, STEAM_PASS (Steam ownership required, no anon install)
# Optional: STEAM_AUTH (Steam Guard code on first login)
#
# Two SteamCMD downloads:
# 1. App 1489630 (CC2 dedicated server, requires login)
# 2. App 1007 (Steamworks SDK Redistributables, anonymous) - provides
#    steamclient.dll/tier0_s.dll/vstdlib_s.dll that CC2 needs but Geometa
#    didn't ship. This is Valve's official redistributable package.

if [[ -z "${STEAM_USER}" || -z "${STEAM_PASS}" ]]; then
    echo "Carrier Command 2 requires Steam credentials (ownership required)."
    echo "Set STEAM_USER and STEAM_PASS on the server before install."
    exit 1
fi

# Fetch SteamCMD
cd /tmp
mkdir -p /mnt/server/steamcmd
curl -sSL -o steamcmd.tar.gz https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz
tar -xzvf steamcmd.tar.gz -C /mnt/server/steamcmd
mkdir -p /mnt/server/steamapps
cd /mnt/server/steamcmd
chown -R root:root /mnt
export HOME=/mnt/server

# Step 1: Install CC2 (App 1489630, requires login, ownership)
echo "===== Installing Carrier Command 2 (App 1489630) ====="
./steamcmd.sh \
  +force_install_dir /mnt/server \
  +login "${STEAM_USER}" "${STEAM_PASS}" "${STEAM_AUTH}" \
  +@sSteamCmdForcePlatformType windows \
  +app_update 1489630 validate \
  +quit

if [[ ! -f /mnt/server/dedicated_server.exe ]]; then
    echo "ERROR: dedicated_server.exe missing after SteamCMD install."
    echo "Likely cause: SteamCMD segfault during client config. Retry the install."
    exit 1
fi

# Step 2: Install Steamworks SDK Redistributables (App 1007, anonymous)
# into a TEMP directory, copy out only the 3 DLLs we need, then delete the
# temp dir. This prevents the runtime image's auto-update from re-validating
# app 1007 against the logged-in account, which pulls the Linux variant and
# wipes the Windows DLLs (causing "failed to initialise SteamGameServer").
echo "===== Installing Steamworks SDK Redistributables (App 1007, isolated) ====="
./steamcmd.sh \
  +force_install_dir /tmp/sdk_redist \
  +login anonymous \
  +@sSteamCmdForcePlatformType windows \
  +app_update 1007 validate \
  +quit

echo "Copying Steamworks SDK DLLs into /mnt/server/..."
for dll in steamclient.dll tier0_s.dll vstdlib_s.dll; do
    found=$(find /tmp/sdk_redist -name "$dll" -print -quit 2>/dev/null)
    if [[ -n "$found" ]]; then
        cp -v "$found" /mnt/server/
    else
        echo "WARN: $dll not found in app 1007 install"
    fi
done

# Clean up temp install dir so no appmanifest_1007.acf exists in
# /home/container/steamapps/ — keeps auto-update away from these DLLs.
rm -rf /tmp/sdk_redist

# Make Linux steamclient.so available via LD_LIBRARY_PATH at runtime
# (AMP's trick - lets Wine use the .so for SteamGameServer auth)
mkdir -p /mnt/server/linux64
cp -v linux64/steamclient.so /mnt/server/linux64/ 2>/dev/null || true

# Create server_config.xml with CC2's exact native format so it isn't regenerated.
# Values are placeholders - the startup command's sed updates them on every start.
cat > /mnt/server/server_config.xml <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<data port="25565" max_players="4" server_name="A Pterodactyl Hosted Server" password="" save_name="" island_count="16" island_count_per_team="1" carrier_count_per_team="1" team_count_ai="1" team_count_human="1" base_difficulty="1" loadout_type="0" blueprints="0" game_data_path="rom_0">
    <permissions/>
    <active_mod_folders/>
</data>
EOF

echo "-----------------------------------------"
echo "Carrier Command 2 install complete."
echo "Server will listen on UDP ${SERVER_PORT} through $((SERVER_PORT + 3))."
echo "-----------------------------------------"