What you need before you start
Five things. Four of them are one-time, and the fifth is the commit you were going to make anyway.
- A Steamworks app.You need an App ID and at least one depot ID. Depot IDs are conventionally your App ID plus one, and Steamworks shows both under SteamPipe in your app's admin pages.
- A Steam builder account. Use a dedicated account with only the Edit App Metadata and Publish permissions, not your personal Steam login. If that account is ever compromised, the blast radius is one app instead of your whole library.
- steamcmd. It ships inside the Steamworks SDK at
tools/ContentBuilder/builder/. Valve's license is yours, not ours, so nobody redistributes it for you. - The engine your project targets. Whatever version your
.uprojectasks for, installed on this machine. - Your project, committed. A build you cannot trace back to a commit is a build you cannot reproduce.
Step 1: get your project ready to package
Packaging writes a lot of files, and none of them belong in source control. If your project already has the standard exclusions, you are done; if not, confirm these are ignored:
Saved/ # transient editor + cook output
Intermediate/ # build intermediates
DerivedDataCache/ # cached derived data
Binaries/ # compiled DLLs
Build/ # packaging outputThis matters more than it looks. A 90-minute cook writing tens of thousands of files into a folder that source control is watching will fight you the entire time. USourceControl checks these exclusions before a build starts and warns loudly if any are missing.
Then set the version you are shipping. Type it once, in the Releases panel, and it goes into three places at the same time: the project's ProjectVersion in Config/DefaultGame.ini, the label on the build, and the build description Steam shows in your build list.
Commit. If you want the rule rather than the habit, turn on require a clean tree and a build will refuse to start while anything is uncommitted.
Step 2: package the build
Open Releases in the desktop app, pick your platform and configuration, and press Build. For a Steam release that is almost always Win64 and Shipping.
Configurations, briefly, because picking the wrong one is a classic first mistake:
- Development keeps the console, logging, and debug features. Right for internal playtests, wrong for players.
- Test is Shipping plus a few profiling and console hooks. Useful for QA and performance passes.
- Shipping strips debug functionality and is what you send to customers.
Preflight runs before the cook does. It checks free disk space, whether Windows long paths are enabled, whether the archive directory is writable, whether the engine your project asks for is installed, and whether packaging output is excluded from source control. Every one of those is a failure that would otherwise surface forty minutes into a build, which is exactly when you have stopped watching.
Then it runs. You will see phases rather than a bar that lies: compiling, cooking, staging, packaging, archiving, verifying. A first Shipping cook on a real project can genuinely take hours, so set a build time limit you are comfortable with. Anything that runs past it is stopped and marked failed rather than holding the machine all night.
Step 3: connect your Steamworks app
In the Releases toolchain settings, browse to your steamcmd.exe. Then press Prepare once.
That step exists for a real reason: steamcmd updates and restarts itself the first time it runs, and its output during that is unparseable. Doing it deliberately now means it does not happen in the middle of your first upload, where it looks exactly like a hang.
Now fill in the Steam target for your project:
- App ID and depot ID from Steamworks.
- Builder account username. A username is not a secret, so it is stored in the clear.
- Password, optionally. steamcmd caches a login token after your first successful sign-in, so a machine that has published before does not need one at all. Store it only if you want unattended uploads to survive that token expiring. It is encrypted by your operating system's keychain and handed to steamcmd over standard input, never on a command line where another process could read it.
- Build description, which is internal metadata Steamworks shows in your build list. Templates like
{version} {platform} {configuration}keep it honest without typing. - File exclusions, if you want to keep things like
*.pdbout of the depot.
Step 4: do a dry run first
Before your first real upload, publish with dry run enabled. It walks the whole path, signs in, resolves your depot, validates the content root, and transfers nothing.
A dry run costs a minute. A failed real upload costs you a half-finished build in your app's history and a conversation with yourself about which build ID is the good one. Do the dry run.
Step 5: upload to Steam
Press Publish, pick Steam, and it goes. The first sign-in on a new machine will ask for a Steam Guard code, which the app prompts for in-window rather than leaving steamcmd waiting on a hidden input nobody can see.
During the upload you get phases rather than silence: authenticating, scanning, uploading, committing, finalizing. Steam does not report a percentage, so the honest signal is how long it has been since steamcmd said anything at all, and that is shown directly. A tool that has been quiet for six minutes is usually working. Now you can tell the difference.
On success you get the Steam build ID, parsed straight out of SteamPipe's output, a link to your Steamworks builds page, and a permanent entry in your publish history with the build label, duration, and log.
Step 6: set the build live (and what Steam won't let a tool do)
Your build is in Steamworks. It is not in front of players yet, and this is the part most tooling glosses over.
You can set a build live on a beta branch from the command line, and USourceControl will do it as part of the upload if you name one. That is how you get a build to your QA testers or your closed beta without touching the web interface.
You cannot set the default branch live from the command line. Valve does not allow it. Promoting a build to your public default branch is a deliberate action in Steamworks App Admin, by a human, on purpose.
So the honest wording is that USourceControl uploads builds. Whether players get them stays yours.
Step 7: stop pressing the button
Once a project has shipped a handful of times, the Publish click is no longer a decision. Turn on auto-publish for the project and any build that succeeds uploads itself to the stores you selected, in order, one at a time.
It stays deliberately cautious. A store that is only half configured is skipped with a sentence naming what is missing, rather than failing the whole chain and leaving the stores that were ready without your build. And it still stops at uploaded, because Steam and Epic both keep the promotion step for a human.
If you want builds to happen without you being at the machine at all, that is a different tool: USourceControl Forge runs on a spare machine, builds on commit, and publishes releases for your team.
The Epic Games Store and itch.io, briefly
The same build goes to the other two stores from the same panel.
Epic Games Storeuploads through BuildPatchTool, which the app can download for you, around 90 MB. You supply your organization, product, and artifact IDs plus a BuildPatchTool client ID and secret, and the path to your game's executable relative to the build root.
Epic's build version must be unique per artifact across platforms, so the same string cannot be both your Windows and your Mac build. The default template encodes the platform for you. And like Steam, self-service organizations make a build live from the Epic Developer Portal, not from the tool.
itch.io is the simplest of the three and the one most indies actually ship to. Butler takes a directory and pushes it to user/game:channel. You need the target, a channel name that tells itch which download to show which operating system, and an API key from your itch.io settings. Butler also reports real progress, so you get an actual percentage and transfer rate rather than an educated guess.
When it goes wrong
The failures you are most likely to hit, and what they mean.
- The build fails minutes in. Usually the engine, the compiler toolchain, or disk space. Preflight catches most of these before the cook starts, so read what it flagged rather than the tail of a 200 MB log.
- Cooking fails on a specific asset. This is your project, not the pipeline. The log names the asset. Open it in the editor and cook that one asset to reproduce quickly.
- steamcmd sits silent for minutes. Usually normal, especially while it walks a large depot. The quiet timer tells you how long it has actually been, so you can wait with confidence instead of killing a working upload.
- Login fails on an unattended run. The cached login token expired. Store a password, or sign in once interactively to refresh the token.
- Long path errors on Windows. Enable long paths. Unreal plus a deep content hierarchy plus a staging directory passes 260 characters faster than you would think. Preflight checks this.
- The upload succeeded but nothing changed for players. Expected. Uploading is not promoting. Go and set the build live in Steamworks or the Epic Developer Portal.
Every build and every upload keeps its own log, readable inside the app, with credentials already redacted. Publish history keeps the build label, duration, bytes moved, exit code, and store link, so "what did we actually ship in March" stays answerable.
Frequently asked
Do I need a Steam Partner account to follow this guide?
Yes. Steam publishing requires a Steamworks account with an app you have already set up, which involves Valve's application fee and their review process. This guide covers everything after that point: packaging your Unreal build and getting it into your app's depot.
Can USourceControl set my game live on Steam automatically?
On a beta branch, yes. On the default branch, no, and not because we chose not to. Valve does not permit setting the default branch live from the command line, so promoting a build to the public branch stays an action in Steamworks App Admin.
Where are my Steam credentials stored?
Encrypted by your operating system's keychain, on your machine only. They are never sent to USourceControl, never placed on a command line where another process on the same Windows session could read them, and never readable back out of the app's interface. Your password goes to steamcmd over standard input.
Do I have to store my Steam password at all?
Usually not. steamcmd caches a login token after your first successful interactive sign-in, so a machine that has published before does not need one. Store a password only if you want unattended auto-publish to keep working after that cached token expires.
How long does a Shipping build take?
On a real Unreal Engine 5 project, expect anywhere from twenty minutes to several hours, depending mostly on shader compilation and how warm your derived data cache is. The first build is always the worst one. Set a build time limit so a genuinely hung build ends instead of holding your machine overnight.
Can I upload to Steam, Epic, and itch.io from one build?
Yes. One packaged build can go to all three, one at a time. If you enable auto-publish, a successful build uploads itself to every store you configured, in order, with anything half-configured skipped and explained rather than failing the run.
Is publishing included on the free plan?
Yes, on every plan including Free. Packaging runs on your machine with your engine, and uploads go from your machine to your store account, so there is nothing for us to meter.
What if I want builds to happen automatically on every commit?
That is USourceControl Forge, a separate app you install on a spare machine and enroll against your organization. It watches for commits, rebuilds the exact committed state, packages it, and publishes a downloadable release for your team, with optional Steam upload from the runner. It is on every plan too.