Digital Software Publishing Stockholm

Home › Unity tools › Guides

What a Unity build needs before it can go on Steam with SteamPipe

ContentBuilder layout, app and depot VDFs, SteamCMD and Steam Guard, what to keep out of the depot, a git hash build stamp, and the same upload from CI.

2026-10-29 · Digital Software · about Ship It

Valve's upload system, SteamPipe, does not take a zip. It takes a folder layout, two small script files and a login through SteamCMD. Here is all of it with nothing but Unity, SteamCMD and a text editor.

The ContentBuilder layout

The Steamworks SDK ships a tools\ContentBuilder folder with these subfolders:

ContentRoot can point anywhere, so your Unity output folder can stay where it is.

The app build script

scripts\app_build_<appid>.vdf is a small key-value file. Valve's example, trimmed to what a Unity game needs:

"AppBuild"
{
    "AppID" "1000"
    "Desc" "Build from commit a1b2c3d"
    "ContentRoot" "C:\Builds\MyGame-windows-client\"
    "BuildOutput" "..\output\"
    "SetLive" ""
    "Depots"
    {
        "1001" "depot_build_1001.vdf"
    }
}

What the fields mean, from Valve's documentation:

The optional Preview "1" writes logs and a manifest and uploads nothing.

The depot build script

"DepotBuild"
{
    "DepotID" "1001"
    "FileMapping"
    {
        "LocalPath" "*"
        "DepotPath" "."
        "Recursive" "1"
    }
    "FileExclusion" "*.pdb"
    "FileExclusion" "*_BackUpThisFolder_ButDontShipItWithYourGame\*"
    "FileExclusion" "*_BurstDebugInformation_DoNotShip\*"
}

Check exclusions with a preview build and its manifest before the real upload.

What must stay out of the depot

The Unity 6.3 manual lists what a Windows IL2CPP build produces. Next to MyGame.exe, MyGame_Data, UnityPlayer.dll and GameAssembly.dll there is a folder named MyGame_BackUpThisFolder_ButDontShipItWithYourGame. It holds the PDB files and the generated C++. The manual says to back it up for every build you ship and not redistribute it: you need it to read crash dumps, players do not.

Separate .pdb files appear when "Copy PDB files" is on in the Build Profiles window; WinPixEventRuntime.dll means a development build.

If your project uses the Burst package, it writes a <build>_BurstDebugInformation_DoNotShip folder alongside the data folder (Burst changelog, 1.6.0-pre.3 and 1.8.23). Same rule: keep it, do not ship it.

The three FileExclusion lines above cover all of this. Also build each release into an empty folder; SteamPipe uploads whatever matches, including leftovers from an earlier development build.

Stamp the build with the git hash

A bug report is only useful if you can tell which commit produced the build. Unity copies everything in Assets/StreamingAssets unmodified into the player output, and Application.streamingAssetsPath points at it at runtime. Write a small JSON file there before the build:

// Assets/Editor/Stamp.cs
using System.Diagnostics;
using System.IO;

public static class Stamp
{
    public static void Write()
    {
        string hash = Git("rev-parse --short HEAD");
        string dirty = Git("status --porcelain").Length > 0 ? "true" : "false";
        Directory.CreateDirectory("Assets/StreamingAssets");
        File.WriteAllText("Assets/StreamingAssets/build-info.json",
            "{\"git\":\"" + hash + "\",\"dirty\":" + dirty + "}");
    }

    static string Git(string args)
    {
        var p = Process.Start(new ProcessStartInfo("git", args)
            { RedirectStandardOutput = true, UseShellExecute = false });
        string s = p.StandardOutput.ReadToEnd().Trim();
        p.WaitForExit();
        return s;
    }
}

Call Stamp.Write() from your build method before BuildPipeline.BuildPlayer, or from a build callback (IPreprocessBuildWithContext runs at the start of a build). Read it at runtime and show the hash on the about screen and in the first log line; the dirty flag marks builds made from uncommitted changes.

Running SteamCMD

From the builder folder, Valve's command is:

.\steamcmd.exe +login <account_name> <password> +run_app_build ..\scripts\app_build_1000.vdf +quit

The script path is relative to the builder folder. SteamCMD updates itself, logs in, splits files into roughly 1 MB chunks, uploads only the new ones, writes a manifest per depot and assigns a BuildID, so a nearly identical build uploads quickly.

Steam Guard and the login token

If login fails with "Account Login Denied", Steam Guard is blocking it. Find the code in the account's email, run set_steam_guard_code <code> in SteamCMD and log in again. SteamCMD then keeps a login token in config\config.vdf; Valve says to preserve that file between runs, and entering the password again issues a new token.

That file is a credential. If SteamCMD lives inside your project tree, add its folder to .gitignore before the first login, not after. The VDF scripts hold no secrets and can be committed.

Setting live on a branch

Uploading changes nothing for players. The build appears on the Builds page in App Admin (partner.steamgames.com/apps/builds) with your Desc. To release it, pick the branch in the "Set build live for branch..." dropdown, click "Preview Change", then "Set Build Live Now". Players on that branch get the update within a couple of minutes.

SetLive skips those clicks for a beta branch only. The default branch, the one customers are on, is set live by hand, and for a released game that needs an extra authorization through the Steam Mobile Authenticator or SMS. An empty SetLive is the safe default: a script can upload as often as it likes, and a person still makes the release decision.

Valve's updating guide suggests: upload, set the build live on a password-protected test branch (set the password first), test through the Steam client, then move the same build to default.

The same steps from CI

Every step above is a command. Unity builds from the command line with -batchmode, which suppresses dialogs, -executeMethod, which runs a static method from a script in an Editor folder, and -quit:

& "C:\Program Files\Unity\Hub\Editor\<version>\Editor\Unity.exe" -batchmode -nographics -quit `
  -projectPath "C:\src\MyGame" -executeMethod BuildScript.BuildWindows -logFile -

-logFile - sends the log to the console. An unhandled exception makes Unity exit with code 1; so does EditorApplication.Exit(1), which is how to report a failed BuildPlayer. Then run the same steamcmd.exe line with the password from a CI secret and the config folder restored from the previous run, so the Steam Guard token is still valid; the first login on a fresh runner still needs a person with the code. With SetLive empty, the pipeline ends with a build waiting on the Builds page.

Where Ship It fits

Ship It is an editor tool from this publisher (39.99 USD, Unity 6.3 LTS and newer, in review on the Asset Store as of 2026-10-29) that does the steps above from a build profile. It writes the app and depot VDFs with the profile's output folder as content root, downloads SteamCMD if you have none, and runs the upload in a visible console where you type the password and the Steam Guard code yourself. It stores no credentials and writes a .gitignore into the SteamCMD folder so the login token never reaches your repository. Debug symbols and Unity's DoNotShip folders are left out of the depot and of the zip, and every build carries a build-info.json with the git hash and a dirty flag, readable at runtime through BuildInfo.Current. A branch is set live only when you name one.

It does not do achievements or stats, store page assets, or itch.io and Epic uploads. If you ship once a year, the manual route above is enough; if you ship every week, or also build servers and run CI, that is what it is for.

Sources

More guides

All guides · Atom feed