Ideas For Docker up.ps1 Scripts

Introduction
When working with Docker, it's easy to get lost in the sauce. There are a lot of moving parts (despite what the marketing says), and it's easy to forget what you need to do to get things up and running. This is especially true if you're switching between projects, some of which use Docker and others that don't.
A Perfect World
Imagine a perfect world where it takes only one command to have a fully functional Sitecore instance running in Docker:
git clone https://github.com/user/sample-project.git && cd sample-project && powershell -File up.ps1
Sound too good to be true? Of course it is!
However, we can get close to this with a solid up.ps1 script. Is it a good idea? Maybe not. But it's fun to think about.
Current Example
Let's see what the latest and greatest example is from Sitecore. This repo is the current example project for XM Cloud and is used by the Sitecore MVP site: https://github.com/Sitecore/XM-Cloud-Introduction/blob/main/up.ps1
The key features and functions are:
- Option to run against edge, vs local, thus affecting the run of Traefik and host containers.
- Build images. Update the XM Cloud base image and build all Sitecore Docker containers.
- Index and push items.
- Open the site and CM in a browser.
- Environment variable loading and validation.
Overall, it's a relatively simple script.
Maximalist Up Script
Some have argued that up.ps1 should do as little as possible. There is something to be said about keeping things simple and letting things fail. YAGNI, the best code is no code, let it crash, etc. However, I would argue that the up.ps1 scripts I have seen don't do enough (or don't exist at all). In order to explore this concept, I will take the idea of writing a maximalist up script to the extreme, listing a series of ideas for what the script could assist with.
Qualities of a maximalist up script:
- Automatically do most things that you would otherwise need to do manually.
- Detect specific errors which are commonly encountered and overcome them in real time or provide helpful error messages.
- Should be able to be run multiple times without issue.
- Functionality is configurable via environment variables and/or script flags.
Here's the list of ideas:
- Check that the .env file exists and is populated and run
init.ps1if needed. - Check if a new version of Docker is available.
- Check that the necessary ports on the host machine aren't already in use.
- Check that IIS is shut off.
- Check that the Docker service / Docker Desktop are installed / running. If not, start them.
- Check that the Dependencies are OK.
- Check that the database server is reachable (if running DBs on host machine).
- Check that the script has been run with the necessary permissions.
- Check validity of custom Sitecore configs to ensure that they are well-formed and don't contain any obvious errors. If bad configs make their way into your containers, it can take a while to track down the issue.
- Check key environment variables.
- Show current version of node, npm, dotnet, PowerShell, Docker, docker compose, etc.
- Show CPU, memory, disk space, etc. to identify potential runtime errors or performance bottlenecks.
- Show the host name of the machine running Docker Desktop.
- Restart Docker Desktop as doing so can fix all kinds of issues.
- Run git commands (pull, etc.).
- Build containers.
- Start the containers.
- Open Sitecore in a browser.
- Open the rendering host in a browser.
- Log in to Sitecore.
- Sync serialized items to Sitecore.
- Populate Solr schema.
- Rebuild indexes.
- Run automated smoke tests to ensure that key functionalities are working as expected.
- Warm up the site by making requests to key pages.
- Call ChatGPT to do... ???
- Print a funny message similar to Sitecore PowerShell Extensions' script execution messages.
- ASCII art.
- Choose your own adventure game.
- Call your parents.
Example Script
Below is an example up.ps1 script that does some of the above. I don't take credit for much of it; it's mostly a soup of other code and ideas I have found online.
# Adapted from https://github.com/ElakkuvanR/Sitecore.Headless.NextJs/blob/master/up.ps1# Updated to use docker compose v2[CmdletBinding(DefaultParameterSetName = "no-arguments")]Param ( [Parameter(HelpMessage = "Run Build")] [switch]$SkipBuild,
[string] $DockerDesktopPath = "C:\Program Files\Docker\Docker\Docker Desktop.exe")
$ErrorActionPreference = "Stop";
$PSVersion = $PSVersionTable.PSVersion.Majorif ($PSVersion -lt 5) { Write-Error "You need at least PowerShell version 5 to run this script." -ForegroundColor Red exit}else { Write-Host "PowerShell version is $($PSVersionTable.PSVersion)." -ForegroundColor Green}
Write-Host "Ensuring that IIS is shut off..."iisreset /stop
Write-Host "Checking Docker..."docker --version
# Start Docker DesktopStart-Process "C:\Program Files\Docker\Docker\Docker Desktop.exe"
$timeout = 60 # Timeout in seconds$startTime = Get-Date$dockerIsReady = $false
do { Start-Sleep -Seconds 2 try { docker info > $null $dockerIsReady = $true } catch { # Ignore errors and continue checking } $currentTime = Get-Date} while (-not $dockerIsReady -and ($currentTime - $startTime).TotalSeconds -lt $timeout)
if ($dockerIsReady) { Write-Host "Docker Desktop is running."}else { Write-Host "Timed out waiting for Docker Desktop to start" exit}
if (Get-Service "com.docker.service" -ErrorAction SilentlyContinue) { Start-Service "com.docker.service"
do { $status = (Get-Service "com.docker.service").Status Start-Sleep -Seconds 2 } while ($status -ne 'Running')
Write-Host "Docker service is running"}else { Write-Host "Docker service is not running / installed" exit}
Write-Host "Stopping running containers prior to re-upping"docker compose down$envContent = Get-Content .env -Encoding UTF8
# Check whether init has been run$envCheck = $envContent | Where-Object { $_ -imatch "^SITECORE_LICENSE=.+" }if (-not $envCheck) { throw "Missing SITECORE_LICENSE environment variable. Did you run 'init.ps1 -InitEnv'?"}
$CM_HOST = ($envContent | Where-Object { $_ -imatch "^CM_HOST=.+" }) -split "=" | Select-Object -Last 1
if ([string]::IsNullOrEmpty($CM_HOST)) { throw "Missing required environment variable. Did you run 'init.ps1 -InitEnv'?"}
Write-Host "CM_HOST:" $CM_HOST
# Tell the user what the PC host name is# We will later want to use the host machine host name instead of IP address for Docker, because VPNs can change IPs, but the host name remains constant$hostMachineHostName = [System.Net.Dns]::GetHostName()Write-Output "Host Machine Hostname: $hostMachineHostName"
if ($Build -eq $true) { # Build all containers in the Sitecore instance, forcing a pull of latest base containers Write-Host "Building containers..." -ForegroundColor Green docker compose build if ($LASTEXITCODE -ne 0) { Write-Error "Container build failed, see errors above." }}
# Start the Sitecore instanceWrite-Host "Starting Sitecore environment..." -ForegroundColor Greendocker compose up -d
# Wait for Traefik to expose CM routeWrite-Host "Waiting for CM to become available..." -ForegroundColor Green$startTime = Get-Datedo { Start-Sleep -Milliseconds 100 try { $status = Invoke-RestMethod "http://localhost:8079/api/http/routers/cm-secure@docker" } catch { if ($_.Exception.Response.StatusCode.value__ -ne "404") { throw } }} while ($status.status -ne "enabled" -and $startTime.AddSeconds(15) -gt (Get-Date))if (-not $status.status -eq "enabled") { $status Write-Error "Timeout waiting for Sitecore CM to become available via Traefik proxy. Check CM container logs."}
try { $dotnetVersion = dotnet --version Write-Host ".NET Core version: $dotnetVersion." -ForegroundColor Green
Write-Host "Restoring Sitecore CLI..." -ForegroundColor Green dotnet tool restore
# If this isn't run, there could be error in the log about a missing sitecore.json file dotnet sitecore init
Write-Host "Logging into Sitecore..." -ForegroundColor Green dotnet sitecore login --cm "https://$CM_HOST/" --auth "https://$ID_HOST/" --allow-write true if ($LASTEXITCODE -ne 0) { Write-Error "Unable to log into Sitecore, did the Sitecore environment start correctly? See logs above." exit }}catch { Write-Host "An error occurred." -ForegroundColor Red exit}
# Write-Host "Populating Solr managed schema..." -ForegroundColor Green# dotnet sitecore index schema-populate# if ($LASTEXITCODE -ne 0) {# Write-Error "Populating Solr managed schema failed, see errors above."# }
# Write-Host "Rebuilding indexes ..." -ForegroundColor Green# dotnet sitecore index rebuild# if ($LASTEXITCODE -ne 0) {# Write-Error "Rebuild indexes failed, see errors above."# }
# Write-Host "Pushing items to Sitecore..." -ForegroundColor Green# dotnet sitecore ser push --publish# if ($LASTEXITCODE -ne 0) {# Write-Error "Serialization push failed, see errors above."# }
Write-Host "Opening site..." -ForegroundColor Green
Start-Process https://$CM_HOST/sitecore/Start-Process "https://$CD_HOST/"
Write-Host ""Write-Host "Use the following command to monitor CM/CD:" -ForegroundColor GreenWrite-Host "docker compose logs -f [cd/cm]"Write-Host ""Conclusion
Personally, I have saved much time and frustration by writing a solid up.ps1 script, so I approve of this post. 😉
If you have any interesting functionality in your up.ps1 scripts, please get in touch!
Keep up.ps1ing your game,
-MG





