Skip to main content

Ephemeral Environments

Ephemeral environments allow you to deploy a temporary, isolated version of your application for testing changes from a Pull Request before merging to main. This is useful for testing features, bug fixes, or integration scenarios in a production-like environment without affecting the main deployment.

Overview

When you trigger an ephemeral deployment:

  1. Your application is built and pushed to Azure Container Registry
  2. A new Pull Request is automatically created in your manifest repository with ephemeral label configuration
  3. Argo CD creates a temporary deployment of your application
  4. Once testing is complete, closing the PR or removing the label cleans up the environment automatically

Prerequisites

Before onboarding to ephemeral environments, ensure:

  • Your application is already deployed to the test environment via Argo CD

Step 1: Create PR Release Workflow

Create a new workflow file .github/workflows/pr-release.yaml in your code repository.

Key Configuration

1. Set up the workflow trigger for PR events:

on:
pull_request:
types: [labeled, synchronize]

This ensures the workflow runs when:

  • The ephemeral-deploy label is added to the PR (labeled)
  • New commits are pushed while the label is present (synchronize)

2. Add the label check in your if conditions:

if: (github.event.action == 'labeled' && github.event.label.name == 'ephemeral-deploy') || (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'ephemeral-deploy'))

3. Add ephemeral-name to the dispatch action:

- name: "Release PR (Ephemeral Test)"
uses: gjensidige/gap-workflow-dispatch-action@main
with:
# ... other fields from your existing workflow ...
ephemeral-name: ${{ github.event.pull_request.number }}

The ephemeral-name parameter uses the PR number to create a unique ephemeral environment for each pull request.

Step 2: Triggering an Ephemeral Deployment

Once your workflow is in place, triggering an ephemeral deployment is simple:

  1. Create a Pull Request in your code repository with your changes
  2. Add the ephemeral-deploy label to the PR
  3. Wait for the workflow to complete

The workflow will:

  • Build your application
  • Push the Docker image to Azure Container Registry
  • Create a PR in your manifest repository with the ephemeral configuration

What happens in the manifest repository

A Pull Request is automatically created in your manifest repository with a title like:

feat(<your-app>/test): ephemeral deploy (name=<PR-number>,app=<your-app>)

This PR contains the updated params.json with your container image and metadata:

{
"container_image": "gjensidige.azurecr.io/<team>/<app>:<tag>@sha256:...",
"container_image_tag": "<tag>",
"github": {
"workflow_actor_username": "<your-username>",
"repo_name": "<your-app>",
"repo_commit_sha": "<commit-sha>",
"manifest_repo_name": "<manifest-repo>",
"manifest_repo_commit_sha": "<manifest-commit>"
}
}

Step 3: Accessing Your Ephemeral Environment

Once Argo CD syncs the changes, your ephemeral application will be available. You can find it in Argo CD with a name pattern:

<your-app>.<pr-number>.apps-int-test

In Argo CD, you'll see two applications:

  • Your main test deployment (e.g., api-commercial-checkout-dk.apps-int-test)
  • Your ephemeral deployment (e.g., api-commercial-checkout-dk.test.33.apps-int-test)

Each ephemeral deployment creates two routing rules, giving you two ways to reach it:

Option 1: Subdomain-based access

A unique subdomain is created for your ephemeral environment using the PR number:

https://<pr-number>.<parent-host>

For example, if your main application is hosted at api-checkout-dk.test.gjensidige.io and your PR number is 33, the ephemeral environment is available at:

https://33.api-checkout-dk.test.gjensidige.io

Option 2: Header-based access

You can also reach your ephemeral environment by sending requests to the original parent hostname with the X-Ephemeral-Name header set to your PR number:

X-Ephemeral-Name: <pr-number>

When this header is present, the request is routed to the ephemeral deployment instead of the main one. When the header is absent, requests continue to reach the main deployment as usual.

tip

Header-based access is especially useful when your API is behind a junction Since header-based routing uses the same parent hostname, the junction forwards the request normally and the routing layer (Traefik) directs it to the ephemeral environment based on the X-Ephemeral-Name header. This means you do not need any junction configuration changes to test against an ephemeral environment.

Updating an Ephemeral Environment

When you push new commits to your PR:

  1. The workflow automatically triggers (due to synchronize event)
  2. A new image is built and pushed
  3. The manifest PR is updated with the new image
  4. Argo CD syncs the changes

As long as the ephemeral-deploy label is present, any new commits will update the ephemeral environment.

Cleaning Up Ephemeral Environments

Ephemeral environments are cleaned up automatically when you no longer need them. There are several ways to trigger cleanup:

From your Code Repository PR

ActionResult
Close the PREphemeral environment is deleted, manifest PR label removed
Remove ephemeral-deploy labelEphemeral environment is deleted, manifest PR label removed
Merge the PREphemeral environment is deleted (merged to main triggers normal deploy)

From the Manifest Repository PR

ActionResult
Close the PREphemeral environment is deleted
Remove the ephemeral labelEphemeral environment is deleted
info

Closing or removing the label from the manifest repository PR will not close your code repository PR—it will only remove the ephemeral-deploy label from it.

Running Tests Against Ephemeral Environments

You can set up a GitHub Actions workflow in your application repository that automatically runs tests whenever an ephemeral environment is deployed. This lets you validate changes in a real environment before merging.

Create a new workflow file .github/workflows/ephemeral-tests.yml in your code repository:

name: "Ephemeral Environment Tests"

on:
status:

permissions:
contents: read
statuses: write

The workflow uses the status event, which fires whenever a commit status is updated on the repository. Argo CD posts a status with context argocd/ephemeral-deploy once it finishes syncing an ephemeral deployment, allowing this workflow to react automatically.

Step 1: Filter for Ephemeral Deployment Success

Add a job that only runs when the Argo CD ephemeral deployment has succeeded:

jobs:
integration-tests:
if: github.event.context == 'argocd/ephemeral-deploy' && github.event.state == 'success'
runs-on: ubuntu-latest

This ensures the workflow is ignored for all other status events and only triggers when the ephemeral environment is ready.

Step 2 (Optional): Set a Pending Status on the PR

This step is optional. It posts a pending commit status that appears as a check on the PR, giving reviewers visibility that integration tests are in progress. If you don't need status checks on the PR, you can skip this step and Step 5.

    steps:
- name: "Set pending status"
uses: actions/github-script@v7
with:
script: |
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.payload.sha,
state: 'pending',
target_url: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`,
context: 'Ephemeral Environment Tests',
description: 'Running integration tests against the ephemeral environment...',
});

Step 3: Check Out the Deployed Commit

Check out the exact commit that was deployed to the ephemeral environment:

      - name: "Git checkout"
uses: actions/checkout@v6
with:
ref: ${{ github.event.sha }}

Using github.event.sha ensures the test code matches the version deployed in the ephemeral environment.

Step 4: Run Integration Tests

Run your test suite against the live ephemeral environment. The Argo CD app URL is available via github.event.target_url:

      - name: "Run integration tests"
id: tests
env:
ARGOCD_URL: ${{ github.event.target_url }}
run: |
echo "Ephemeral environment is ready"
echo "ArgoCD app: $ARGOCD_URL"
# Add your integration test commands here

Replace the placeholder comment with your actual test commands (e.g. mvn verify, npm test, curl checks, etc.).

Step 5 (Optional): Report the Test Result

This step is optional and pairs with Step 2. It posts a final success or failure commit status on the PR so reviewers can see the test outcome directly in the PR checks. If you skipped Step 2, you can skip this step as well.

      - name: "Update status"
if: always()
uses: actions/github-script@v7
with:
script: |
const state = '${{ steps.tests.outcome }}' === 'success' ? 'success' : 'failure';
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.payload.sha,
state,
target_url: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`,
context: 'Ephemeral Environment Tests',
description: state === 'success'
? 'All integration tests passed'
: 'Integration tests failed',
});

The if: always() ensures the status is updated even if the tests fail, so you always get clear feedback on the PR.