This guide shows you how to deploy your application using Optimizely Frontend Hosting, both manually and via CI/CD automation.
Follow these steps to deploy manually using PowerShell.
Install-Module -Name EpiCloud -Scope CurrentUser -Force Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser Import-Module EpiCloud
$projectId = "<your_project_id>" $clientKey = "<your_client_key>" $clientSecret = "<your_client_secret>" $targetEnvironment = "Test1" # Or "Production" or "Test2"
Use this naming format:
<name>.head.app.<version>.zip
Example:
optimizely-one.head.app.20250714.zip
โ ๏ธ If the filename does not contain ".head.app.", the system will treat it as a .NET NuGet package!
Connect-EpiCloud -ProjectId $projectId -ClientKey $clientKey -ClientSecret $clientSecret $sasUrl = Get-EpiDeploymentPackageLocation Add-EpiDeploymentPackage -SasUrl $sasUrl -Path .\optimizely-one.head.app.20250714.zip Start-EpiDeployment ` -DeploymentPackage "optimizely-one.head.app.20250714.zip" ` -TargetEnvironment $targetEnvironment ` -DirectDeploy ` -Wait ` -Verbose
Once youโve done a few manual deployments, automate the process via script:
Add a .zipignore file to your project root to exclude unnecessary files.
Example .zipignore:
.next node_modules .env .git .vscode .DS_Store
Update:
Youโll need:
Set them in PowerShell like so:
$env:OPTI_PROJECT_ID = "<your_project_id>" $env:OPTI_CLIENT_KEY = "<your_client_key>" $env:OPTI_CLIENT_SECRET = "<your_client_secret>"
Or add them to your system environment variables.
Save the script as deploy-optimizely.ps1 and run:
.\deploy-optimizely.ps1
if (-not $env:OPTI_PROJECT_ID -or -not $env:OPTI_CLIENT_KEY -or -not $env:OPTI_CLIENT_SECRET) { Write-Host "Missing environment variables." -ForegroundColor Red exit 1 } Install-Module -Name EpiCloud -Scope CurrentUser -Force -ErrorAction SilentlyContinue Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force Import-Module EpiCloud $projectId = $env:OPTI_PROJECT_ID $clientKey = $env:OPTI_CLIENT_KEY $clientSecret = $env:OPTI_CLIENT_SECRET $targetEnvironment = "Test1" $sourcePath = "C:\Workspace\Personal\optimizely-one" $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" $zipName = "optimizely-one.head.app.$timestamp.zip" $zipPath = ".\$zipName" if (Test-Path $zipPath) { Remove-Item $zipPath } $zipIgnorePath = Join-Path $sourcePath ".zipignore" $excludeRoot = @() if (Test-Path $zipIgnorePath) { $excludeRoot = Get-Content $zipIgnorePath | Where-Object { $_ -and $_ -notmatch "^#" } } $rootExcludes = $excludeRoot | ForEach-Object { Join-Path $sourcePath $_ } $includeFiles = Get-ChildItem -Path $sourcePath -Recurse -File | Where-Object { foreach ($ex in $rootExcludes) { if ($_.FullName -like "$ex\*" -or $_.FullName -eq $ex) { return $false } } return $true } if ($includeFiles.Count -eq 0) { Write-Host "ERROR: No files to archive." -ForegroundColor Red exit 1 } $tempPath = Join-Path $env:TEMP "nextjs-build-zip" if (Test-Path $tempPath) { Remove-Item -Recurse -Force $tempPath } New-Item -ItemType Directory -Path $tempPath | Out-Null foreach ($file in $includeFiles) { $relativePath = $file.FullName.Substring($sourcePath.Length).TrimStart('\') $destPath = Join-Path $tempPath $relativePath $destDir = Split-Path -Path $destPath -Parent if (-not (Test-Path -LiteralPath $destDir)) { New-Item -ItemType Directory -Path $destDir -Force | Out-Null } Copy-Item -LiteralPath $file.FullName -Destination $destPath -Force } Compress-Archive -Path "$tempPath\*" -DestinationPath $zipPath Remove-Item -Recurse -Force $tempPath Connect-EpiCloud -ProjectId $projectId -ClientKey $clientKey -ClientSecret $clientSecret $sasUrl = Get-EpiDeploymentPackageLocation Add-EpiDeploymentPackage -SasUrl $sasUrl -Path $zipPath Start-EpiDeployment ` -DeploymentPackage $zipName ` -TargetEnvironment $targetEnvironment ` -DirectDeploy ` -Wait ` -Verbose
Go to GitHub โ Repo โ Settings โ Secrets โ Actions โ New Repository Secret
| Name | Value |
|---|---|
OPTI_PROJECT_ID | Your Optimizely Project ID |
OPTI_CLIENT_KEY | Your Optimizely Client Key |
OPTI_CLIENT_SECRET | Your Optimizely Client Secret |
Save this as scripts/deploy.ps1 in your repo:
if (-not $env:OPTI_PROJECT_ID -or -not $env:OPTI_CLIENT_KEY -or -not $env:OPTI_CLIENT_SECRET) { Write-Host "Missing required environment variables." -ForegroundColor Red exit 1 } Install-Module -Name EpiCloud -Scope CurrentUser -Force -ErrorAction SilentlyContinue Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force Import-Module EpiCloud $projectId = $env:OPTI_PROJECT_ID $clientKey = $env:OPTI_CLIENT_KEY $clientSecret = $env:OPTI_CLIENT_SECRET $targetEnvironment = "Test1" $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" $zipName = "optimizely-one-github.head.app.$timestamp.zip" $zipPath = ".\$zipName" Compress-Archive -Path * -DestinationPath $zipPath if (-not (Test-Path $zipPath)) { Write-Host "Failed to create ZIP file." -ForegroundColor Red exit 1 } Connect-EpiCloud -ProjectId $projectId -ClientKey $clientKey -ClientSecret $clientSecret $sasUrl = Get-EpiDeploymentPackageLocation Add-EpiDeploymentPackage -SasUrl $sasUrl -Path $zipPath Start-EpiDeployment ` -DeploymentPackage $zipName ` -TargetEnvironment $targetEnvironment ` -Wait
Create a file .github/workflows/deploy.yml:
name: Deploy to Optimizely on: push: branches: - main jobs: deploy: runs-on: windows-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Run deployment script shell: pwsh run: ./scripts/deploy.ps1 env: OPTI_PROJECT_ID: ${{ secrets.OPTI_PROJECT_ID }} OPTI_CLIENT_KEY: ${{ secrets.OPTI_CLIENT_KEY }} OPTI_CLIENT_SECRET: ${{ secrets.OPTI_CLIENT_SECRET }}
| Deployment Type | Tools Used | Recommended For |
|---|---|---|
| Manual | PowerShell | First-time deployments, debugging |
| Automated | GitHub CI/CD + PowerShell | Production deployments, automation |
Once you're confident with manual deployments, switch to CI/CD for consistency and speed ๐