GitHub Actions workflows don't import. You rebuild them as .gitlab-ci.yml. The scripts, build commands, and Docker images mostly stay as-is — most of the work is translating YAML. AI-Assisted Migration walks through prompt patterns that handle this conversion well.

Concept mapping

GitHub Actions GitLab CI/CD
.github/workflows/*.yml .gitlab-ci.yml
Workflow Pipeline
Job Job
Step (within a job) Line in script:
Workflow event triggers rules: / workflow:
Actions Marketplace CI/CD Components
Secrets CI/CD Variables (Settings → CI/CD → Variables)

Stages run sequentially; jobs in a stage run in parallel. needs: overrides stage order for explicit DAGs.

Syntax conversion

Concept GitHub Actions GitLab CI/CD
Checkout uses: actions/checkout@v4 Automatic (control with GIT_STRATEGY)
Environment variable ${{ env.VAR }} $VAR or ${VAR}
Conditional if: github.ref == 'refs/heads/main' rules: - if: $CI_COMMIT_BRANCH == "main"
Artifact actions/upload-artifact@v4 artifacts: { paths: [dist/] }
Cache actions/cache@v4 cache: { key, paths }
Job dependency needs: [build] needs: [build]
Container container: node:18 image: node:18
Matrix strategy.matrix parallel.matrix

Migration steps

  1. Inventory. List workflows in .github/workflows/. Convert one at a time.
  2. Create .gitlab-ci.yml at the repository root.
  3. Define stages matching the workflow's job graph.
  4. Translate jobs. Each job becomes a top-level YAML key with stage, image, and script.
  5. Move secrets. Recreate GitHub Secrets as CI/CD Variables under Settings → CI/CD → Variables. Mark sensitive values Masked and (where possible) Protected.
  6. Validate. Use your-gitlab-instance/-/ci/lint or the CI/CD → Editor.
  7. Run. Commit, watch the first pipeline, fix what breaks.

Examples

Build and test

# GitHub Actions
name: CI
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '18' }
      - run: npm install
      - run: npm test
      - run: npm run build
# .gitlab-ci.yml
stages: [test, build]

test:
  stage: test
  image: node:18
  script:
    - npm install
    - npm test

build:
  stage: build
  image: node:18
  script:
    - npm install
    - npm run build
  artifacts:
    paths: [dist/]

Matrix build

# GitHub Actions
jobs:
  test:
    strategy:
      matrix:
        node-version: [16, 18, 20]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: ${{ matrix.node-version }} }
      - run: npm test
# .gitlab-ci.yml
test:
  parallel:
    matrix:
      - NODE_VERSION: ['16', '18', '20']
  image: node:${NODE_VERSION}
  script:
    - npm install
    - npm test

Conditional deploy

# GitHub Actions
deploy:
  if: github.ref == 'refs/heads/main'
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - run: ./deploy.sh
# .gitlab-ci.yml
deploy:
  stage: deploy
  script: ./deploy.sh
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

Docker build and push (GitLab container registry)

docker:
  stage: build
  image: docker:latest
  services: [docker:dind]
  script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
    - docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA" .
    - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"

Common Action equivalents

GitHub Action GitLab equivalent
actions/checkout@v4 Built-in
actions/setup-node@v4 image: node:18
actions/setup-python@v5 image: python:3.11
actions/cache@v4 cache: keyword
actions/upload-artifact@v4 artifacts: keyword
actions/download-artifact@v4 dependencies: or needs:
docker/build-push-action@v5 Docker commands with the docker:dind service

Useful built-in variables

$CI_COMMIT_BRANCH        Current branch
$CI_COMMIT_SHA           Commit SHA
$CI_PROJECT_NAME         Project name
$CI_PROJECT_DIR          Working directory
$CI_REGISTRY             GitLab container registry URL
$CI_REGISTRY_IMAGE       Full image path for this project
$CI_PIPELINE_ID          Pipeline ID
$CI_JOB_NAME             Current job name

Full reference: Predefined variables.

Pitfalls

Common pitfalls

  • Recreate GitHub Secrets as CI/CD Variables before running pipelines. A missing variable surfaces as a runtime failure, not a config error.
  • Not every Action has a direct equivalent. Bespoke shell or a CI/CD Component is usually cleaner than chasing a one-to-one mapping.
  • Required status checks aren't imported. Recreate them as merge request approval rules.