Skip to main content
RMUX environments are durable cloud VMs. CI runners can create ephemeral workspaces inside them to run tests, then clean up. The environment stays warm between runs, so Docker images, build caches, and databases are already there.
CI never creates or destroys environments. A developer provisions the environment once. CI only creates and removes workspaces.

Why Use RMUX for CI

RMUX workspaces skip the setup tax that ephemeral CI runners pay on every run:
StepGitHub runnerRMUX workspace
Checkout repo~15s~5s (git worktree)
Install dependencies60-120s (cold)10-20s (warm cache)
Start services (Docker Compose)120-300s (pull images)0s (already running)
DB migrations / seed30-60s0s (already done)
Total setup3-8 min~15-30s
This adds up. For a team running 50 PRs/day with a heavy environment, RMUX saves 200+ runner minutes per day. The compute cost is sunk since the environment is already running for development. RMUX CI is most useful when:
  • Your project has a complex dev environment (Docker Compose stacks, databases, GPUs)
  • Environment setup time dominates test execution time
  • You need VPC access or internal services that runners can’t reach
  • You want dev/CI parity (tests run in the same environment developers use)

Setup

1

Create a CI token

Create a service token for the CI runner. This only needs to happen once.
rmux token create --kind ci --label "github-actions"
Copy the token value for the next step.
2

Add GitHub secrets

In your repo settings, add two secrets:
SecretValue
RMUX_CI_TOKENThe token from step 1
RMUX_ENVIRONMENTEnvironment name (e.g. my-project) or ID (e.g. env_abc123)
The environment must already be provisioned and in ready state.
3

Add the workflow

Create .github/workflows/test.yml in your repository.

Example Workflow

.github/workflows/test.yml
name: Test
on:
  pull_request:
    types: [opened, synchronize, reopened]

concurrency:
  group: rmux-${{ github.event.pull_request.number }}
  cancel-in-progress: true

env:
  RMUX_API_KEY: ${{ secrets.RMUX_CI_TOKEN }}
  RMUX_ENVIRONMENT: ${{ secrets.RMUX_ENVIRONMENT }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install RMUX
        run: curl -fsSL https://get.remotemux.com | bash

      - name: Create workspace
        run: rmux workspace new "ci-pr-${{ github.event.pull_request.number }}"

      - name: Run tests
        run: |
          rmux workspace run -w "ci-pr-${{ github.event.pull_request.number }}" \
            "cd /workspace && npm ci && npm test"

      - name: Cleanup
        if: always()
        run: rmux workspace rm "ci-pr-${{ github.event.pull_request.number }}" --force
Each workspace new creates a git worktree on the remote environment. Multiple PRs can run concurrently since each workspace is isolated at the filesystem level.

Environment Resolution

Workspace commands resolve the target environment in this order:
  1. --env flag (explicit override)
  2. RMUX_ENVIRONMENT env var
  3. .rmux/state.json directory binding (local dev)
In CI, there is no directory binding. Set RMUX_ENVIRONMENT once in the workflow env block and all workspace commands pick it up. You can also override per-command:
rmux workspace ls --env staging
rmux workspace new ci-run --env my-other-project
Both environment names and IDs (env_*) are accepted.

Cleaning Up Stale Workspaces

If a CI runner is killed or GitHub has an outage, the cleanup step may not run. Use workspace prune to remove orphaned workspaces.

Manual prune

# See what would be removed
rmux workspace prune --prefix "ci-" --stale 2h --dry-run

# Remove them
rmux workspace prune --prefix "ci-" --stale 2h --force

Scheduled prune

Add a cron workflow to your repository:
.github/workflows/prune.yml
name: Prune stale CI workspaces
on:
  schedule:
    - cron: '0 */6 * * *'

env:
  RMUX_API_KEY: ${{ secrets.RMUX_CI_TOKEN }}
  RMUX_ENVIRONMENT: ${{ secrets.RMUX_ENVIRONMENT }}

jobs:
  prune:
    runs-on: ubuntu-latest
    steps:
      - name: Install RMUX
        run: curl -fsSL https://get.remotemux.com | bash

      - name: Prune stale workspaces
        run: rmux workspace prune --prefix "ci-" --stale 2h --force

Prune options

OptionDescription
--prefix <prefix>Only prune workspaces whose name starts with this prefix
--stale <duration>Only prune workspaces idle longer than this duration (30s, 5m, 2h, 1d)
--dry-runList candidates without deleting
-f, --forceSkip the confirmation prompt (required in non-TTY / CI)
Root workspaces are never pruned regardless of filters.

Concurrency

Multiple CI workspaces share a single VM. They are isolated at the filesystem level (separate git worktrees) but share CPU, memory, disk, and the Docker daemon. Shared resources can be an advantage: Docker layer caches, node_modules caches, and pre-started services are available to all workspaces without redundant setup. If concurrent CI runs overwhelm the VM, size the environment appropriately or use GitHub Actions concurrency groups to limit parallel runs.
Last modified on March 30, 2026