Redis®*: GitHub Actions & GitLab CI

Launch a real Redis service from your GitHub Actions or GitLab CI pipeline, run your tests against it, and automatically tear it down

👋 Welcome to the Stackhero documentation!

Stackhero offers a ready-to-use Redis cloud solution that provides a host of benefits, including:

  • Redis Commander web UI included.
  • Unlimited message size and transfers.
  • Effortless updates with just a click.
  • Optimal performance and robust security powered by a private and dedicated VM.

Save time and simplify your life: it only takes 5 minutes to try Stackhero's Redis cloud hosting solution!

This guide explains how to run a real, dedicated Redis service inside your CI pipeline using either GitHub Actions or GitLab CI. By following these steps, you can test your code against a live Redis instance in conditions that closely resemble production, so you no longer need to rely on mocks or simulations. Redis is a blazing-fast in-memory data store used as a cache, key-value store and message broker.

Each time your pipeline runs, a new Redis instance is created. This means your tests interact with the same type of service your users will experience in production. The workflow automatically creates a temporary stack, adds Redis, waits for it to be ready, retrieves the generated credentials, runs a smoke test using redis-cli, and always cleans everything up at the end of the run.

You will use the Stackhero CLI, a standalone command-line tool that makes it quick and easy to launch and manage Stackhero services.

To allow the CLI to work non-interactively, you will need an access token (format: usr-xxxxxx:tokenId). You only need to create this token once, then add it to your CI pipeline as a secure, encrypted secret.

  1. Create the token: In your Stackhero dashboard, click your profile picture at the top right, go to Your account, then Access tokens, and click Create token.
  2. For GitHub Actions: In your repository, go to Settings > Secrets and variables > Actions > New repository secret, and enter the token as STACKHERO_TOKEN.
  3. For GitLab CI: In your project, go to Settings > CI/CD > Variables > Add variable, set the key to STACKHERO_TOKEN, and check Masked (and Protected if your CI only runs on protected branches).

Never include your access token directly in your pipeline YAML file. If it is present in YAML, it could be exposed to anyone with repository access and may appear in build logs. Storing it as a CI secret ensures it remains encrypted and masked, keeping your token secure.

Here is a complete example script that manages the entire service lifecycle. It shows how simple the setup is, with just a few commands. You can copy the ready-to-use YAML for your platform from the sections below.

#!/bin/bash
set -euo pipefail

# STACKHERO_TOKEN is supplied by the CI secret, the CLI picks it up automatically.

stackName="ci-redis-$$"          # Unique stack name for each run
serviceStore="redis"             # The Redis service store
instance="1G"        # Change this as needed (see step 3)
region="europe"                         # Region name (see stackhero regions-list)

# 1. Install the CLI on the runner
curl -fsSL https://www.stackhero.io/install.sh | sh

# 2. Install the client required for smoke testing (jq + curl are the baseline)
apt-get update && apt-get install -y --no-install-recommends jq curl redis-tools

# 3. Create a dedicated stack for this run
stackId=$(stackhero --format=script stack-create --name="$stackName")
echo "Stack created: $stackId"

# 4. Add Redis and capture its service id
serviceId=$(stackhero --format=script service-add \
  --stack="$stackId" \
  --service-store="$serviceStore" \
  --instance="$instance" \
  --region="$region")
echo "Service added: $serviceId"

# 5. Wait until the service is running (this may take a couple of minutes)
stackhero service-wait-for --service="$serviceId"

# 6. Read the configuration (contains the generated credentials)
config=$(stackhero service-configuration-get --service="$serviceId" --format=json)

# 7. Extract the credentials you need
host=$(echo "$config" | jq -r '.configuration.domain')
password=$(echo "$config" | jq -r '.configuration.credentials.password')

# 8. Smoke test: Send a PING and expect a PONG.
redis-cli -h "$host" -p 6380 -a "$password" --tls --no-auth-warning PING | grep -q PONG

echo "✅ Redis is reachable from CI."

The teardown step, which deletes the service, waits for its removal, and then deletes the stack, is detailed in the platform-specific sections below. This approach ensures cleanup always happens, even if a smoke test fails.

A stack can only be deleted once it is empty. Always delete the service first and wait for its removal, then delete the stack. The service-wait-for command ensures the service is either running or deleted before proceeding, making it the right tool for deletion waits as well.

The examples below use the entry-level instance 1G for Redis by default. This is a reliable choice for most workloads, but you can adjust it as needed. To see all available instance types for Redis, run:

# The NAME column shows the value to pass to --instance
stackhero instances-store-list --service-store=redis

To get started, save the following as .github/workflows/ci.yml. From now on, every push and pull request will run tests against a real Redis instance.

name: CI with Redis

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    env:
      STACKHERO_TOKEN: ${{ secrets.STACKHERO_TOKEN }}
      STACK_NAME: ci-redis-${{ github.run_id }}-${{ github.run_attempt }}
      INSTANCE: "1G"   # Change this as needed (see step 3)
      REGION: europe
    steps:
      - uses: actions/checkout@v4

      - name: Install the Stackhero CLI and the client
        run: |
          curl -fsSL https://www.stackhero.io/install.sh | sh
          apt-get update && apt-get install -y --no-install-recommends jq curl redis-tools

      - name: Create the Redis service
        run: |
          set -euo pipefail
          STACK_ID=$(stackhero --format=script stack-create --name="$STACK_NAME")
          echo "STACK_ID=$STACK_ID" >> "$GITHUB_ENV"
          SERVICE_ID=$(stackhero --format=script service-add \
            --stack="$STACK_ID" \
            --service-store="redis" \
            --instance="$INSTANCE" \
            --region="$REGION")
          echo "SERVICE_ID=$SERVICE_ID" >> "$GITHUB_ENV"
          stackhero service-wait-for --service="$SERVICE_ID"

      - name: Run tests against Redis
        run: |
          set -euo pipefail
          config=$(stackhero service-configuration-get --service="$SERVICE_ID" --format=json)
          host=$(echo "$config" | jq -r '.configuration.domain')
password=$(echo "$config" | jq -r '.configuration.credentials.password')
          # Send a PING and expect a PONG.
          redis-cli -h "$host" -p 6380 -a "$password" --tls --no-auth-warning PING | grep -q PONG
          echo "✅ Redis is reachable from CI."
          # You can run your own test suite here using the credentials above ...

      - name: Tear down (always, even on failure)
        if: always()
        run: |
          if [ -n "${SERVICE_ID:-}" ]; then
            stackhero service-delete --service="$SERVICE_ID" --confirm
            stackhero service-wait-for --service="$SERVICE_ID"
          fi
          if [ -n "${STACK_ID:-}" ]; then
            stackhero stack-delete --stack="$STACK_ID" --confirm
          fi

The teardown step uses if: always() to ensure it runs in all cases, making sure your Redis instance is deleted and you are not billed for unused resources.

You can save this configuration as .gitlab-ci.yml. With this setup, each pipeline run spins up a fresh real Redis for your tests.

test:
  image: ubuntu:24.04
  variables:
    STACK_NAME: "ci-redis-$CI_PIPELINE_ID-$CI_JOB_ID"
    INSTANCE: "1G"   # Change this as needed (see step 3)
    REGION: "europe"
    SERVICE_STORE: "redis"
  # STACKHERO_TOKEN comes from the CI/CD variable you created in step 1.
  script:
    - set -euo pipefail
    - curl -fsSL https://www.stackhero.io/install.sh | sh
    - apt-get update && apt-get install -y --no-install-recommends jq curl redis-tools
    - STACK_ID=$(stackhero --format=script stack-create --name="$STACK_NAME")
    - echo "STACK_ID=$STACK_ID" >> deploy.env
    - SERVICE_ID=$(stackhero --format=script service-add --stack="$STACK_ID" --service-store="$SERVICE_STORE" --instance="$INSTANCE" --region="$REGION")
    - echo "SERVICE_ID=$SERVICE_ID" >> deploy.env
    - stackhero service-wait-for --service="$SERVICE_ID"
    - config=$(stackhero service-configuration-get --service="$SERVICE_ID" --format=json)
    - host=$(echo "$config" | jq -r '.configuration.domain')
password=$(echo "$config" | jq -r '.configuration.credentials.password')
    # Send a PING and expect a PONG.
    - redis-cli -h "$host" -p 6380 -a "$password" --tls --no-auth-warning PING | grep -q PONG
    - echo "✅ Redis is reachable from CI."
    # You can run your own test suite here using the credentials above ...
  after_script:
    - test -f deploy.env && . ./deploy.env || true
    - >
      if [ -n "${SERVICE_ID:-}" ]; then
        stackhero service-delete --service="$SERVICE_ID" --confirm
        stackhero service-wait-for --service="$SERVICE_ID"
      fi
    - >
      if [ -n "${STACK_ID:-}" ]; then
        stackhero stack-delete --stack="$STACK_ID" --confirm
      fi

On GitLab, cleanup is handled in after_script. This section is always executed, even if the job fails, ensuring your Redis resources are deleted and you are not billed for unused resources.

In GitLab, after_script runs in a fresh shell. To handle this, the script writes the service and stack IDs to deploy.env during the job and reloads them before cleanup. This ensures that even if something fails mid-job, your resources are still deleted.

That is the complete CI lifecycle for Redis: create a stack, add the service, wait, retrieve credentials, smoke-test, and always tear down. Each pipeline run gets a real, isolated service, with nothing left running when you are done. For more information about available commands and non-interactive STACKHERO_TOKEN authentication, you can refer to the full CLI documentation.