GitHub Actions → GitLab CI converter

Whole estate? Fixed-fee audit
Everything runs in your browser. Nothing is uploaded.
.github/workflows/ci.yml
.gitlab-ci.yml

    

Useful? This tool is free and has no ads. If it saved you an afternoon, a small sponsorship keeps it maintained.

What this converter does

Paste a GitHub Actions workflow and you get a .gitlab-ci.yml plus a list of everything that did not map cleanly. It is the same idea as the other converters on this site, run the other way: for teams moving from GitHub to GitLab (gitlab.com or self-managed), or keeping both. Conversion runs entirely in your browser; nothing is uploaded.

The output is a starting point, not a finished migration. Marketplace actions become the shell commands or GitLab keywords that replace them; anything without a clean equivalent becomes a TODO step that fails loudly, and the notes list the CI/CD variables you need to create.

How GitHub Actions concepts map to GitLab CI

GitHub ActionsGitLab CINotes
on: push / pull_request / tagsworkflow: rulesBranch and tag filters become $CI_COMMIT_BRANCH / $CI_COMMIT_TAG rules; paths becomes changes. Push + pull_request gets the standard duplicate-pipeline guard.
schedule, workflow_dispatch, release$CI_PIPELINE_SOURCE rulesCron schedules are created in the UI; inputs become top-level variables with descriptions and options.
jobs.<id> + needsjobs + needs (DAG)stages are derived from the dependency depth so the pipeline graph reads the same way.
runs-on: ubuntu-*default: imagePick the image in the options. Windows and macOS jobs get the gitlab.com SaaS runner tags; self-hosted labels become tags.
container, servicesimage, servicesService env becomes variables; localhost:port in the job is rewritten to the service alias.
strategy.matrixparallel: matrixKeys become upper-case variables; include rows are appended; object-valued dimensions are flattened; runs-on: ${{ matrix.os }} on Linux-only matrices is dropped.
if: on jobsrules: ifgithub.ref, github.event_name, startsWith/contains, inputs, env and secrets comparisons; always() / failure() become when.
if: on stepsshell if / after_scriptalways() and failure() steps move to after_script; other conditions become shell tests.
${{ secrets.X }}, ${{ vars.X }}, ${{ env.X }}$XThe notes list every CI/CD variable to create (secrets masked).
${{ github.* }}, $GITHUB_*$CI_*40+ context values mapped: sha, ref, repository, run id, actor, workspace, PR number, head/base refs, commit message…
$GITHUB_OUTPUT, $GITHUB_ENV, job outputsbuild.env + artifacts: reports: dotenvLater jobs read the variables directly; $GITHUB_PATH becomes export PATH.
actions/upload-artifact / download-artifactartifacts: paths / needsretention-days becomes expire_in; downloads become needs on the producing job.
actions/cachecache: key: fileshashFiles() keys become file-based keys; restore-keys become fallback_keys; restore/save become policy.
actions/setup-node|python|go|java|dotnet, ruby/setup-ruby, Rust, PHP, Bun, Deno, Elixir, Flutter, Terraform…image:The job runs in the official image for that version; cache: npm|pip|maven|gradle becomes a cache block.
docker/login-action, setup-buildx, build-push-actiondocker:27 + dind serviceghcr.io/owner/repo becomes $CI_REGISTRY_IMAGE; GHA cache becomes a registry cache.
aws-actions/configure-aws-credentials, google-github-actions/auth, azure/loginid_tokens + CLI loginOIDC roles become GITLAB_OIDC_TOKEN exchanges; static keys become CI/CD variables.
softprops/action-gh-release, ncipollo/release-actionrelease:With the release-cli image and a tag rule; assets need package-registry links.
peaceiris/actions-gh-pages, actions/deploy-pagespages jobThe output directory is moved to public/.
JUnit reporters, Codecov, Sonar, Slack, SSH deploys, PyPI/npm publish, semantic-release, GoReleaser, pre-commit, golangci-lint…shell commands / artifacts: reportsAbout 100 actions have a direct mapping; the rest become TODO steps with the inputs quoted.
github/codeql-action, Trivy, TruffleHog, dependency reviewinclude: template:GitLab's SAST, Container Scanning, Secret Detection and Dependency Scanning templates.
concurrency, timeout-minutes, continue-on-error, environmentinterruptible / resource_group, timeout, allow_failure, environment
Reusable workflows (jobs.x.uses)trigger: include:Child pipeline; convert the called workflow separately. Local composite actions become TODOs.
actions/stale, labeler, release-drafter, Dependabot helpersdropped with a noteRepository automation, not CI.

Things to check by hand

FAQ

Is my workflow uploaded anywhere?

No. The page is static and the converter runs in your tab. Read actions-gitlab.js on GitHub.

Can I run it on every workflow in a repository?

Yes: npx jenkins-to-actions convert .github/workflows/ci.yml --to gitlab prints the .gitlab-ci.yml; convert each workflow and combine them with include: local:.

Why is the output valid but not identical in behaviour?

The two systems differ in places that no converter can paper over: caches must live inside the project directory, services are reached by alias rather than localhost, and matrix rows cannot be extended. Each of those is called out in the notes with the fix.

Going the other way?

Every other page on this site converts to GitHub Actions: Jenkinsfile, GitLab CI, Azure Pipelines, CircleCI, Bitbucket Pipelines and Travis CI.

Migrating a whole organisation?

The same converters ship as a command-line audit. Point it at a GitHub organisation, a GitLab group, an Azure DevOps organisation, a Bitbucket workspace or a Jenkins server and it inventories every CI configuration it finds, converts what it can, estimates the effort per repository and lists the secrets to create. With --open-prs it opens a pull request in each GitHub repository with the workflow and the notes. Nothing is sent anywhere except the platform you point it at.

GITHUB_TOKEN=… npx jenkins-to-actions audit --github-org acme          # report + workflows
GITHUB_TOKEN=… npx jenkins-to-actions audit --github-org acme --open-prs   # one PR per repository
GITLAB_TOKEN=… npx jenkins-to-actions audit --gitlab-group acme/platform
AZURE_DEVOPS_TOKEN=… npx jenkins-to-actions audit --azure-org acme
npx jenkins-to-actions audit --bitbucket-workspace acme --user me --token APP_PASSWORD
npx jenkins-to-actions audit --url https://jenkins.example.com --user me --token APITOKEN

See a real result: this pull request was opened by one command on a repository holding one config per system. The README has the details, and the migration page describes the fixed-fee audit if you would rather have it done for you. Once you are on Actions, the cost calculator and the free CI Cost Report action show what the minutes cost.