Unreal Engine Build Setup Guide
Configure automated Unreal Engine builds with full pipeline visibility.
Table of contents
- Overview
- Prerequisites
- Build Script Example
- UAT BuildCookRun Parameters
- Jenkins Integration
- Steam Upload
- Complete Pipeline
- Troubleshooting
- Best Practices
Overview
This guide walks you through setting up automated Unreal Engine builds that integrate with ButterStack’s 5-stage pipeline. You’ll learn how to:
- Configure build scripts for Windows and Linux
- Send build events to ButterStack
- Package for Steam deployment
- Track the complete asset lineage
Prerequisites
- Unreal Engine 5.x installed
- Jenkins or other CI system (optional)
- ButterStack project with integrations configured
- Perforce or Git for source control
Build Script Example
Here’s a complete PowerShell script for building an Unreal project for Steam:
# Unreal Engine Steam Build Script
# This script builds, cooks, and packages your UE project for Steam distribution
param(
[string]$ProjectPath = "C:\Projects\MyGame\MyGame.uproject",
[string]$UE5Root = "C:\Program Files\Epic Games\UE_5.4",
[string]$OutputDir = "C:\Projects\MyGame\SteamBuild",
[string]$Platform = "Win64",
[string]$Configuration = "Shipping"
)
# ButterStack configuration (set via environment or parameters)
$ButterStackUrl = $env:BUTTERSTACK_URL
$ButterStackToken = $env:BUTTERSTACK_TOKEN
$ProjectId = $env:BUTTERSTACK_PROJECT_ID
$BuildNumber = $env:BUILD_NUMBER
$UAT = Join-Path $UE5Root "Engine\Build\BatchFiles\RunUAT.bat"
# Helper function to send ButterStack notifications
function Send-ButterStackEvent {
param(
[string]$Endpoint,
[hashtable]$Body
)
if ($ButterStackUrl -and $ButterStackToken) {
try {
$json = $Body | ConvertTo-Json -Depth 10
Invoke-RestMethod -Uri "$ButterStackUrl/webhooks/game_engine/$Endpoint" `
-Method Post -Body $json -ContentType "application/json" `
-Headers @{ Authorization = "Bearer $ButterStackToken" }
Write-Host "Sent event: $Endpoint" -ForegroundColor Gray
} catch {
Write-Warning "Failed to send ButterStack event: $_"
}
}
}
# Check if Unreal Editor is running (will cause build failures)
$unrealProcesses = Get-Process -Name "UnrealEditor*" -ErrorAction SilentlyContinue
if ($unrealProcesses) {
Write-Host "Unreal Editor is running. Close it before building." -ForegroundColor Red
$response = Read-Host "Close it now? (y/n)"
if ($response -eq 'y') {
$unrealProcesses | Stop-Process -Force
Start-Sleep -Seconds 3
} else {
Write-Host "Build cancelled."
exit 1
}
}
# Validate project exists
if (-not (Test-Path $ProjectPath)) {
Write-Error "Project not found: $ProjectPath"
exit 1
}
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Unreal Engine Build for Steam" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Project: $ProjectPath"
Write-Host "Platform: $Platform"
Write-Host "Configuration: $Configuration"
Write-Host "Output: $OutputDir"
Write-Host ""
# Notify ButterStack - Cooking Started
Send-ButterStackEvent -Endpoint "cooking_started" -Body @{
project_id = $ProjectId
build_number = [int]$BuildNumber
engine_version = "5.4"
platform = $Platform
configuration = $Configuration
}
$startTime = Get-Date
# Run the build
& $UAT BuildCookRun `
-project="$ProjectPath" `
-noP4 `
-platform=$Platform `
-clientconfig=$Configuration `
-cook `
-build `
-stage `
-pak `
-compressed `
-prereqs `
-distribution `
-nodebuginfo `
-archive `
-archivedirectory="$OutputDir"
$exitCode = $LASTEXITCODE
$endTime = Get-Date
$duration = ($endTime - $startTime).TotalMilliseconds
if ($exitCode -eq 0) {
Write-Host ""
Write-Host "========================================" -ForegroundColor Green
Write-Host "Build Completed Successfully" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host "Duration: $([math]::Round($duration / 1000 / 60, 2)) minutes"
Write-Host "Output: $OutputDir\Windows"
Write-Host ""
# Calculate PAK size
$pakPath = Join-Path $OutputDir "Windows\MyGame\Content\Paks"
$pakSize = 0
if (Test-Path $pakPath) {
$pakSize = (Get-ChildItem $pakPath -Recurse | Measure-Object -Property Length -Sum).Sum
}
# Notify ButterStack - Build Completed
Send-ButterStackEvent -Endpoint "build_completed" -Body @{
project_id = $ProjectId
build_number = [int]$BuildNumber
pak_size = $pakSize
total_cook_time_ms = [int]$duration
}
# Open output folder
explorer.exe "$OutputDir"
} else {
Write-Host ""
Write-Host "========================================" -ForegroundColor Red
Write-Host "Build Failed" -ForegroundColor Red
Write-Host "========================================" -ForegroundColor Red
Write-Host "Exit code: $exitCode"
# Notify ButterStack - Build Failed
Send-ButterStackEvent -Endpoint "build_failed" -Body @{
project_id = $ProjectId
build_number = [int]$BuildNumber
phase = "packaging"
error = "Build failed with exit code $exitCode"
}
exit $exitCode
}
UAT BuildCookRun Parameters
Understanding the build parameters:
| Parameter | Description |
|---|---|
-project | Path to .uproject file |
-noP4 | Don’t use Perforce (use Git or skip VCS) |
-platform | Target platform (Win64, Linux, Android, etc.) |
-clientconfig | Build configuration (Development, Shipping, Debug) |
-cook | Cook content for the target platform |
-build | Compile game code |
-stage | Stage files to intermediate directory |
-pak | Package content into PAK files |
-compressed | Compress PAK files |
-prereqs | Include prerequisites installer |
-distribution | Build for distribution (removes development features) |
-nodebuginfo | Exclude debug symbols (smaller build) |
-archive | Copy final build to archive directory |
-archivedirectory | Where to put the final build |
Jenkins Integration
Jenkinsfile for Unreal Builds
pipeline {
agent {
label 'windows-ue5'
}
environment {
UE_ROOT = 'C:\\Program Files\\Epic Games\\UE_5.4'
PROJECT_PATH = 'C:\\Projects\\MyGame\\MyGame.uproject'
BUTTERSTACK_URL = 'https://your-butterstack.com'
BUTTERSTACK_TOKEN = credentials('butterstack-token')
BUTTERSTACK_PROJECT_ID = 'your-project-id'
}
parameters {
choice(name: 'PLATFORM', choices: ['Win64', 'Linux'], description: 'Target platform')
choice(name: 'CONFIG', choices: ['Shipping', 'Development'], description: 'Build configuration')
}
stages {
stage('Notify Start') {
steps {
script {
httpRequest(
url: "${BUTTERSTACK_URL}/webhooks/game_engine/cooking_started",
httpMode: 'POST',
contentType: 'APPLICATION_JSON',
customHeaders: [[name: 'Authorization', value: "Bearer ${BUTTERSTACK_TOKEN}"]],
requestBody: """
{
"project_id": "${BUTTERSTACK_PROJECT_ID}",
"build_number": ${BUILD_NUMBER},
"engine_version": "5.4",
"platform": "${params.PLATFORM}",
"configuration": "${params.CONFIG}"
}
"""
)
}
}
}
stage('Build') {
steps {
bat """
"%UE_ROOT%\\Engine\\Build\\BatchFiles\\RunUAT.bat" BuildCookRun ^
-project="%PROJECT_PATH%" ^
-noP4 ^
-platform=%PLATFORM% ^
-clientconfig=%CONFIG% ^
-cook -build -stage -pak ^
-compressed -prereqs -distribution ^
-archive -archivedirectory="%WORKSPACE%\\Build"
"""
}
}
stage('Notify Complete') {
steps {
script {
httpRequest(
url: "${BUTTERSTACK_URL}/webhooks/game_engine/build_completed",
httpMode: 'POST',
contentType: 'APPLICATION_JSON',
customHeaders: [[name: 'Authorization', value: "Bearer ${BUTTERSTACK_TOKEN}"]],
requestBody: """
{
"project_id": "${BUTTERSTACK_PROJECT_ID}",
"build_number": ${BUILD_NUMBER}
}
"""
)
}
}
}
stage('Archive') {
steps {
archiveArtifacts artifacts: 'Build/**/*', fingerprint: true
}
}
}
post {
failure {
script {
httpRequest(
url: "${BUTTERSTACK_URL}/webhooks/game_engine/build_failed",
httpMode: 'POST',
contentType: 'APPLICATION_JSON',
customHeaders: [[name: 'Authorization', value: "Bearer ${BUTTERSTACK_TOKEN}"]],
requestBody: """
{
"project_id": "${BUTTERSTACK_PROJECT_ID}",
"build_number": ${BUILD_NUMBER},
"error": "Jenkins build failed"
}
"""
)
}
}
}
}
Steam Upload
After building, upload to Steam using SteamCMD:
app_build.vdf Template
"AppBuild"
{
"AppID" "YOUR_APP_ID"
"Desc" "Build ${BUILD_NUMBER} - ${GIT_COMMIT_MESSAGE}"
"ContentRoot" "./Build/Windows"
"BuildOutput" "./SteamOutput"
"Depots"
{
"YOUR_DEPOT_ID"
{
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}
}
}
Upload Script
# Upload to Steam
$SteamCmd = "C:\SteamCMD\steamcmd.exe"
$SteamUser = $env:STEAM_USER
$SteamPass = $env:STEAM_PASS
& $SteamCmd +login $SteamUser $SteamPass +run_app_build app_build.vdf +quit
if ($LASTEXITCODE -eq 0) {
# Notify ButterStack of deployment
Send-ButterStackEvent -Endpoint "../deployment" -Body @{
project_id = $ProjectId
platform = "steam"
event = "uploaded"
build_number = [int]$BuildNumber
}
}
Complete Pipeline
With everything configured, your pipeline looks like:
1. Developer commits to Perforce with "PROJ-123: Added new level #ci"
└─▶ ButterStack receives webhook, creates Change record
└─▶ Perforce triggers Jenkins build
2. Jenkins starts build
└─▶ ButterStack receives build_started webhook
└─▶ Build appears in ButterStack with "In Progress" status
3. UAT BuildCookRun executes
└─▶ ButterStack receives cooking_started webhook
└─▶ Game Engine Build record created
4. Build completes successfully
└─▶ ButterStack receives build_completed webhook
└─▶ Build status updated to "Completed"
5. SteamCMD uploads to Steam
└─▶ ButterStack receives deployment webhook
└─▶ Deployment record created
6. ButterStack polls Steam API
└─▶ Detects build is live
└─▶ Deployment status updated to "Live"
Complete lineage: Task → Commit → Build → Engine Build → Deployment
Troubleshooting
Build Fails Immediately
- Check Unreal Editor isn’t running
- Verify project path is correct
- Ensure UAT.bat exists at expected location
Cooking Takes Too Long
- Enable Derived Data Cache (DDC)
- Use shared DDC on network drive
- Consider incremental cooking
PAK Files Too Large
- Enable
-compressedflag - Review what’s being packaged
- Use pak chunking for DLC
Steam Upload Fails
- Verify SteamCMD credentials
- Check depot configuration
- Ensure build output exists
Best Practices
- Use dedicated build machines - Don’t build on development workstations
- Enable DDC - Dramatically speeds up cooking
- Version your build scripts - Keep scripts in source control
- Tag builds consistently - Use BUILD_NUMBER for traceability
- Archive build logs - Keep logs for debugging
- Test locally first - Run build script manually before CI