Steam Deployment¶
Summary: Builds are uploaded to Steam via
steamcmd(SteamPipe) using a generatedapp_buildVDF. Dev and release builds upload automatically and go live on the non-defaultdevelopmentSteam branch; a standalone workflow allows manual uploads of existing builds. Login uses credentials cached on the runner — no password lives in CI.
Table of Contents¶
- Architecture Overview
- App / Depot IDs
- Authentication (One-Time Runner Setup)
- Upload Flow
- Steam Branches
- Standalone Upload Workflow
- Troubleshooting
- Related Systems
- Source References
- Recent Changes
Architecture Overview¶
┌─────────────────────┐ ┌──────────────────────┐ ┌──────────────┐
│ Packaged Build │ │ steamcmd │ │ Steamworks │
│ C:\BuildArtifacts\ │──────▶│ run_app_build │──────▶│ (SteamPipe) │
│ <run_number>\ │ │ (generated VDF) │ │ │
└─────────────────────┘ └──────────────────────┘ └──────────────┘
│
│ cached credentials (config.vdf on runner)
▼
┌──────────────────────┐
│ GitHub Secrets │
│ STEAM_USERNAME │
└──────────────────────┘
- steamcmd:
C:\Tools\Steamworks\sdk\tools\ContentBuilder\builder\steamcmd.exe(Steamworks SDK 1.65, self-updates on run) - Build output/cache:
C:\SteamBuildOutput(chunk cache — keep it, it speeds up delta uploads) - The
app_buildVDF is generated per run into$RUNNER_TEMPbecauseContentRootandDescchange every build. There is no committed VDF.
App / Depot IDs¶
| Item | Value |
|---|---|
| App ID | 5091420 |
| Windows depot | 5091421 (Steamworks default depot, appid+1) |
| Depot mapping | * recursive from the packaged build root (same dir the EGS upload uses) |
Authentication (One-Time Runner Setup)¶
CI uses a dedicated builder account (username in the STEAM_USERNAME GitHub secret; Steam account registered to builder@eternal-server.net, a Cloudflare Email Routing forward). Steam Guard blocks unattended password logins, so the account must log in interactively once on the runner machine, as the same Windows user the runner runs as:
& C:\Tools\Steamworks\sdk\tools\ContentBuilder\builder\steamcmd.exe +login <builder_username>
# enter password + Steam Guard code when prompted, then `quit`
This caches a login token in the builder's config/config.vdf; subsequent +login <username> (no password) works unattended. The token survives reboots but is invalidated if the account's password changes or the token expires (rare) — the fix is always to re-run the interactive login.
Builder account requirements in Steamworks (Users & Permissions): - Edit App Metadata + Publish App Changes on app 5091420. Nothing else.
Upload Flow¶
Automated (via build-and-package.yml, upload-steam job)¶
Build Completes
│
▼
Upload Job Runs? ──No──▶ (main branch: skip)
│
Yes (dev, release/*, or upload_steam input)
│
▼
Generate app_build VDF (AppID 5091420, depot 5091421, SetLive development)
│
▼
steamcmd +login (cached) +run_app_build ──▶ chunks, uploads, sets live on 'development'
│
▼
Discord Notification
Runs in parallel with the upload-egs job on the same triggers.
Version Format¶
Same as EGS: YYYY.MM.DD-<run>-<sha> automated, manual-<build_number>-<run> standalone. The version string goes into the build's Desc, visible in Steamworks build history.
Steam Branches¶
SetLive "development"in the VDF makes each upload immediately live on thedevelopmentbranch — created in App Admin > SteamPipe > Builds ("Create new app branch"; can be password-protected). Opt into it in the Steam client via Properties > Betas.- Branch names need at least 4 characters (a branch literally named
devis rejected) and no spaces. - The
defaultbranch can NOT be set live from steamcmd. Promoting a build to default is always a manual click in Steamworks > SteamPipe > Builds ("Set build live for branch..." dropdown > Preview Change > Set Build Live Now). - The branch UI only appears usable once at least one build has been uploaded — the very first upload must go up with
SetLiveempty (upload-only), then the branch is created and set live by hand. - Unlike EGS labels, Steam builds don't auto-delete; old builds stay in build history.
Standalone Upload Workflow¶
For uploading existing builds without rebuilding — GitHub Actions > "Upload to Steam (standalone)" > Run workflow:
| Input | Description | Default |
|---|---|---|
build_number |
Folder name in C:\BuildArtifacts\ |
Required |
steam_branch |
Branch to set live (empty = upload only) | development |
Troubleshooting¶
| Error | Cause | Fix |
|---|---|---|
FAILED (Invalid Password) / login hangs waiting for Guard code |
Cached token missing/expired, or job runs as a different Windows user than the one that cached it | Re-run the one-time interactive login on the runner |
Failed to commit build / App state is invalid |
First-time SteamPipe setup incomplete | In Steamworks: create the depot, add it to the default launch config, publish once |
Trying to set live branch 'default' error |
steamcmd cannot set default live | Use a non-default branch, promote manually |
| Set-live fails naming a missing branch | Branch doesn't exist (or was renamed) | Create it in SteamPipe > Builds; branch names: ≥4 chars, no spaces |
| Upload succeeds but build not visible in client | Branch not opted into | Steam client > game Properties > Betas > select development |
| Slow full re-upload every time | C:\SteamBuildOutput was deleted |
Leave the cache directory in place |
Related Systems¶
- CI/CD Pipeline — Build pipeline overview
- EGS Deployment — The parallel Epic upload path
- Infrastructure Setup — Runner and machine configuration
Source References¶
| File | Purpose |
|---|---|
.github/workflows/build-and-package.yml |
Automated upload after build (upload-steam job) |
.github/workflows/upload-steam.yml |
Standalone manual upload workflow |
Recent Changes¶
| Date | Change | Impact |
|---|---|---|
| 2026-08-11 | Branch renamed to development (Steam requires ≥4-char branch names) |
CI sets live on development |
| 2026-08-10 | Initial Steam deployment (SDK 1.65, steamcmd, development-branch auto-set-live) | Steam uploads mirror the EGS flow |