Migrating from GitHub Actions
Bring your workflows as they are. rickub reads the same .github/workflows files, runs the same actions, and evaluates the same expressions — this page covers what moves untouched, what you have to move by hand, and what genuinely differs.
A migration is three steps: push the repository, re-create your secrets, push a commit. You do not rewrite your workflow YAML — rickub parses GitHub Actions workflows, not a dialect of them. The same .github/workflows/ directory that runs on GitHub runs here.
That is not a claim we make from a spec: rickub builds and ships itself from workflow files that run on both hosts. The same repository's files are gated on github.server_url so each host runs its half — one set of YAML, two CI systems, no fork of the config.
What runs unchanged
Workflows live where they already live — .github/workflows/*.yml (rickub also reads .rickub/workflows/, should you prefer that). Everything in this list is parsed and honoured with GitHub's semantics:
- Triggers —
push(branches, tags, path filters),pull_request(dispatched when the merge request opens and re-run on every push to its head branch, including a push to a fork's branch),workflow_dispatchwith typed inputs,schedulecron entries, andrelease.types:, branch and path filters all apply. - Job graph —
needs:ordering, joboutputs:,if:conditions includingfailure()andalways(), andcontinue-on-error. - Matrices —
strategy.matrixwithinclude/exclude,fail-fast, andmax-parallel. - Environment —
env:at workflow, job and step level,defaults.run.shell/working-directory, andtimeout-minutes. - Reusable workflows —
jobs.<id>.usesagainst aworkflow_callworkflow, withwith:inputs andsecrets:(includinginherit). - Service containers —
services:and container jobs run against a real Docker daemon inside the job's machine. Apostgres:16-alpineservice is exactly how rickub's own test jobs get their database. - Step summaries — anything a step appends to
$GITHUB_STEP_SUMMARYis rendered on the run page.
The actions you already use
actions/checkout— pinned to a commit SHA or written as@v4, either is fine, because the version is never consulted: rickub clones your repository onto the job's work disk before the first step runs, so the tree is already there when checkout looks for it. (A checkout of a different repository —with: repository:— does run a real clone, and does not yet carry credentials, so a private cross-repository checkout won't work.)- Node-based actions —
actions/setup-go,setup-node,setup-pythonand the rest of the node20 ecosystem run as they do on a GitHub-hosted runner. - Composite actions from your own repository —
uses: ./.github/actions/whateverresolves against the checked-out tree. - Docker actions and plain
docker build— on a docker-capable runner class (see below),docker/login-action,docker buildanddocker pushall work.
Keep your SHA pins. An action is fetched at the exact ref you pinned, so a supply-chain posture you already enforce travels with the file.
Runner classes
runs-on: resolves against rickub's runner catalog. Every job runs in its own Linux x86-64 microVM, thrown away when the job ends.
| runs-on | Machine | Docker | CI minutes |
|---|---|---|---|
ubuntu-latestaliases: ubuntu-24.04, ubuntu-22.04, ubuntu, linux | Standard rickub-hosted runner | Not guaranteed | 1× |
largealias: ubuntu-latest-large | 4 vCPU · 8 GiB, 8 GiB scratch | Yes — a Docker daemon inside the job | 2× |
Ask for large when a job builds or runs containers. A job that names no runs-on: gets the repository's default class from Settings → Actions.
windows-latest and macos-latest are not available, and are deliberately not aliased to Linux: a job asking for one fails immediately with the list of supported labels rather than queueing forever waiting for a runner that is never coming.
timeout-minutes: is honoured per job. A job that declares none gets 60 minutes; the ceiling for a job that declares one is 360 minutes.
Secrets
Secrets do not travel with a git push — this is the one thing you genuinely have to move by hand. Re-create them under the repository's Settings → Secrets and variables (organizations have org-level secrets too, scoped to the repositories an owner has allowed). Non-sensitive values go in Variables and resolve as ${{ vars.NAME }}.
Secret names may not start with GITHUB_ or RICKUB_ — those prefixes belong to the values rickub injects into every job, so the form rejects them. If a workflow reads a secret called something like GITHUB_DEPLOY_KEY, rename it (for example to DEPLOY_KEY) in both the settings page and the YAML.
About secrets.GITHUB_TOKEN
Every job still gets a GITHUB_TOKEN — minted per job, scoped to the repository, masked in logs, and resolvable as ${{ secrets.GITHUB_TOKEN }} and ${{ github.token }}. It authorizes rickub's GitHub-compatible REST endpoints, so actions that cut releases, comment on merge requests or read contents work unmodified.
It is not a github.com credential. rickub's GITHUB_TOKEN cannot authenticate to github.com or to ghcr.io — it is signed by rickub and github.com has never heard of it. This is the classic migration trap: a job that pushed to GHCR with password: ${{ secrets.GITHUB_TOKEN }} will fail on authentication here. For anything on GitHub's side, bring your own PAT and store it as a rickub secret.
Pushing to GHCR from a rickub job, with a classic PAT that has write:packages:
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: your-github-user
password: ${{ secrets.GHCR_PAT }} # a github.com PAT you createdThe default token is read-only. A job that writes must say so with a permissions: block (contents: write, issues: write, …) — the same grant you would declare on GitHub, and it travels with the workflow file.
The registry
If your workflow builds images, the shortest migration is to point them at registry.rickub.com — because then there is no login step at all. Every trusted job gets a per-job registry credential injected as RICKUB_REGISTRY_TOKEN, the host as RICKUB_REGISTRY_HOST, and the job's Docker is logged in before the first step runs.
jobs:
image:
runs-on: large
permissions:
packages: write # needed the first time a name is pushed
steps:
- uses: actions/checkout@v4
- run: docker build -t "$RICKUB_REGISTRY_HOST/acme/api:${{ github.sha }}" .
- run: docker push "$RICKUB_REGISTRY_HOST/acme/api:${{ github.sha }}"permissions: packages: write is what lets a job claim a new image name — the first push binds that name to this repository. Without it a job may only push names already bound to its repository. See the Container registry page for visibility, bindings and deploy tokens.
Jobs from forks get no registry credential at all, by design — see what's different.
Artifacts and cache
actions/upload-artifact, actions/download-artifact and actions/cache work unchanged, against rickub's own endpoints. Uploaded artifacts are listed and downloadable from the run page. The limits:
- 2 GiB per artifact, 100 artifacts and 10 GiB per run.
- 90-day artifact retention. A shorter
retention-days:is honoured; a longer one is clamped to 90. - 10 GiB of cache per repository, evicted least-recently-used when it fills, and an entry unused for 7 days is swept. Cache entries are scoped per branch, exactly as on GitHub.
What's different
Everything below is a real gap or a real difference. Read this section before you cut over a deploy pipeline.
| Feature | On rickub |
|---|---|
environment: | Silently ignored — the key is dropped when the job is read. There are no environment protection rules, required reviewers, or wait timers, so a job that names an environment simply runs. Do not leave one in place as an approval gate; see the pattern below. |
Job-level concurrency: | Ignored. Workflow-level concurrency: works: runs share a group, and cancel-in-progress: true cancels the group's earlier in-flight runs. Group values are matched literally, except the common ${{ github.ref }} idiom, which is resolved per ref. |
| Less common triggers | push, pull_request, workflow_dispatch, schedule, release and workflow_call fire. Others — issue_comment, pull_request_target, repository_dispatch, check_run, merge_group and friends — are not wired up yet. A workflow triggered only by one of those never fires; it does not error, it is simply quiet. Check that your workflow list on the Actions tab looks the way you expect after the first push. |
GITHUB_TOKEN | Valid for rickub only. Never for github.com or ghcr.io. |
GITHUB_API_URL | Points at rickub's GitHub-compatible REST endpoints — a subset covering repositories, releases and assets, issue comments, refs, contents and pull creation. Actions outside that surface will get a 404 from a real endpoint list, not a silent wrong answer. |
GITHUB_SERVER_URL | Carries a host form the official artifact and cache clients accept; ${{ github.server_url }} and RICKUB_GITHUB_SERVER_URL carry your real rickub URL. |
github.repository_owner | The rickub-side owner handle. Anything that hardcodes your GitHub org name needs updating. |
| Fork pull requests | Run untrusted: no secrets, a read-only token, no registry credential, and — by default — held until a maintainer approves the run. Configurable under Settings → Actions. |
| Runner platforms | Linux x86-64 only. No Windows, no macOS, no self-hosted labels. |
| CI minutes | Metered against your plan's monthly allowance; large is published at 2× weight. An exhausted allowance blocks dispatch and leaves a red commit status on the pushed commit. |
Gating a deploy without environment:
Since environment approvals are not enforced, gate the deploy on something that is: the presence of a secret. Seeding the secret is the deliberate act that turns deployment on; deleting it turns deployment off — and no workflow edit is involved either way.
- name: Deploy
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: |
set -uo pipefail
if [ -z "${DEPLOY_TOKEN}" ]; then
echo "DEPLOY_TOKEN is not set — continuous deployment is OFF."
exit 0
fi
set -e
./deploy.shThis is the pattern rickub uses on itself. The absence of the secret is the off switch, and it is visible in one place — the repository's secrets page — instead of buried in a settings screen nobody reads.
Migration checklist
- Push the repository — or import it, which brings code, issues and pull requests along.
- Leave
.github/workflows/alone. Resist the urge to rewrite; start from what already works. - Re-create your secrets under Settings → Secrets and variables, renaming anything that starts with
GITHUB_orRICKUB_(in the settings page and in the YAML). - Pick runner classes — add
runs-on: largeto any job that builds or runs containers; leave the rest onubuntu-latest. - Re-point image pushes at
registry.rickub.com(no login step, addpermissions: packages: write) — or keep pushing to GHCR with your own PAT stored as a secret. - Audit anything that talked to github.com —
GITHUB_TOKEN,github.repository_owner, hardcoded org names. - Replace
environment:gates with the secret-presence pattern above. - Push a commit and watch it run under the repository's Actions tab. Fix forward from a real run, not from a guess.
Never gonna give your pipeline up: if a workflow behaves differently here than it did on GitHub, that is a bug worth reporting, not a rewrite you owe us.