Docs
Continuous Deployment
Guides

Continuous deployment

For CI, keep state remote, gate environments by branch, and let the pipeline run lithos deploy.

Two things matter most: remote state so every runner sees the same graph, and branch-based gates so only main (or your release branch) can touch production.

Prerequisites

  • Remote state configured for the project.

  • An Open Cloud API key with the right scopes — see Authentication.

  • A ROBLOSECURITY cookie with permission to manage the experiences this project owns.

  • A lithos.yml whose prod environment has branches: [main]:

    lithos.yml
    environments:
      - label: dev
        targetNamePrefix: environmentLabel
        branches: [develop]
      - label: prod
        targetAccess: public
        branches: [main]

Secrets

Add four repository secrets (Settings → Secrets and variables → Actions):

SecretValue
ROBLOSECURITYYour Roblox cookie.
LITHOS_OPEN_CLOUD_API_KEYYour Open Cloud key.
LITHOS_AWS_ACCESS_KEY_IDS3 / R2 access key.
LITHOS_AWS_SECRET_ACCESS_KEYS3 / R2 secret.
⚠️

Use LITHOS_* in new CI and team docs so your workflow matches the current examples. Legacy MANTLE_* variables are still accepted for compatibility.

A minimal workflow

.github/workflows/deploy.yml
name: Deploy
 
on:
  push:
    branches: [main, develop]
  workflow_dispatch:
 
jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
 
    steps:
      - uses: actions/checkout@v4
 
      - name: Install Foreman
        uses: Roblox/setup-foreman@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
 
      - name: Pick environment
        id: env
        run: |
          if [ "${GITHUB_REF}" = "refs/heads/main" ]; then
            echo "label=prod" >> "$GITHUB_OUTPUT"
          else
            echo "label=dev" >> "$GITHUB_OUTPUT"
          fi
 
      - name: Deploy
        env:
          ROBLOSECURITY: ${{ secrets.ROBLOSECURITY }}
          LITHOS_OPEN_CLOUD_API_KEY: ${{ secrets.LITHOS_OPEN_CLOUD_API_KEY }}
          LITHOS_AWS_ACCESS_KEY_ID: ${{ secrets.LITHOS_AWS_ACCESS_KEY_ID }}
          LITHOS_AWS_SECRET_ACCESS_KEY: ${{ secrets.LITHOS_AWS_SECRET_ACCESS_KEY }}
        run: lithos deploy --environment ${{ steps.env.outputs.label }} --plain-preview

Notes:

  • --plain-preview removes ANSI escape sequences for log viewers. Non-TTY runs auto-approve after printing the plan.
  • Preflight still runs and fails early on missing keys, wrong universe scope, and similar issues.
  • Keep the branch-to-environment mapping in the workflow and the branches: guard in lithos.yml.
  • Foreman pins the Lithos version used by CI.

If a deploy finishes in a bad state, run lithos undo --environment <label> --plain-preview against the same remote state backend.

Pull-request previews

For pull requests, run lithos diff --live and post or archive the result. It shows planned changes and surfaces drift without applying.

.github/workflows/diff.yml
name: Diff
 
on:
  pull_request:
    branches: [main, develop]
 
jobs:
  diff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: Roblox/setup-foreman@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
 
      - name: Diff
        env:
          ROBLOSECURITY: ${{ secrets.ROBLOSECURITY }}
          LITHOS_OPEN_CLOUD_API_KEY: ${{ secrets.LITHOS_OPEN_CLOUD_API_KEY }}
          LITHOS_AWS_ACCESS_KEY_ID: ${{ secrets.LITHOS_AWS_ACCESS_KEY_ID }}
          LITHOS_AWS_SECRET_ACCESS_KEY: ${{ secrets.LITHOS_AWS_SECRET_ACCESS_KEY }}
        run: lithos diff --environment dev --live --plain-preview

--live is useful here because it catches dashboard edits before they surprise the next deploy.

Next steps