---
layout: 'page'
uri: '/deployment/ci-cd-integration'
position: 3
slug: 'deployment-ci-cd-integration'
parent: 'deployment'
navTitle: 'CI/CD integration'
title: 'CI/CD integration'
description: 'Validate and deploy documentation automatically in your CI/CD pipeline.'
---
# CI/CD integration
The same [Dockerfile](/deployment/docker-setup) you use locally serves two roles in CI/CD:
1. **Lint job** — validates docs on every pull/merge request. Catches broken links, invalid frontmatter, and other issues before review.
2. **Deploy job** — builds, pushes, and deploys the production image on merge to main branch.
The key insight: `docker build` runs `lint`, `import`, and `vectorize` at build time. If any validation fails, the build fails — broken docs never reach production.
## Prerequisites
- Docker with BuildKit enabled (default in Docker 23+)
- The [Dockerfile](/deployment/docker-setup) at `docker/documan/Dockerfile`
- CI secrets configured (see [Setting up secrets](#setting-up-secrets))
Do not use `SKIP_BUILD_STEPS=true` in CI — that flag is only for the initial local setup when docs may not have valid frontmatter yet.
## GitHub Actions
```yaml
name: Documan
on:
pull_request:
push:
branches: [main]
jobs:
lint:
name: Lint documentation
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- name: Validate docs
run: |
docker build \
-f docker/documan/Dockerfile \
-t documan-lint:latest .
deploy:
name: Build and push
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Log in to registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
run: |
docker build \
-f docker/documan/Dockerfile \
--secret id=DOCUMAN_LICENSE_KEY,env=DOCUMAN_LICENSE_KEY \
--secret id=DOCUMAN_OPENAI_API_KEY,env=DOCUMAN_OPENAI_API_KEY \
-t ghcr.io/${{ github.repository }}/documan:latest .
docker push ghcr.io/${{ github.repository }}/documan:latest
env:
DOCUMAN_LICENSE_KEY: ${{ secrets.DOCUMAN_LICENSE_KEY }}
DOCUMAN_OPENAI_API_KEY: ${{ secrets.DOCUMAN_OPENAI_API_KEY }}
```
## GitLab CI
```yaml
lint-documan:
stage: test
script:
- |
docker build \
-f docker/documan/Dockerfile \
-t documan-lint:latest .
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
build-documan:
stage: build
variables:
IMAGE_NAME: "$CI_REGISTRY_IMAGE/documan"
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
script:
- |
docker build \
-f docker/documan/Dockerfile \
--secret id=DOCUMAN_LICENSE_KEY,env=DOCUMAN_LICENSE_KEY \
--secret id=DOCUMAN_OPENAI_API_KEY,env=DOCUMAN_OPENAI_API_KEY \
-t "${IMAGE_NAME}:latest" .
docker push "${IMAGE_NAME}:latest"
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
```
## How it works
### Lint job (pull/merge requests)
Runs `docker build` which triggers `lint` and `import` inside the Dockerfile. No secrets are needed — `vectorize` is automatically skipped when `DOCUMAN_OPENAI_API_KEY` is not provided, and `import` works without `DOCUMAN_LICENSE_KEY` for projects with up to 100 files.
If your project has more than 100 files, add `DOCUMAN_LICENSE_KEY` to the lint job as well.
### Deploy job (merge to main)
Runs the full build with secrets — `lint`, `import`, and `vectorize`. Pushes the image to your container registry. Your infrastructure pulls the latest image and restarts the service.
## Setting up secrets
Both secrets are optional. `DOCUMAN_LICENSE_KEY` is only needed for projects with more than 100 files. `DOCUMAN_OPENAI_API_KEY` enables semantic search — without it, vectorize is skipped.
### GitHub Actions
Go to your repository **Settings > Secrets and variables > Actions** and add:
- `DOCUMAN_LICENSE_KEY` — your license key (required for >100 files)
- `DOCUMAN_OPENAI_API_KEY` — your OpenAI API key (enables semantic search)
### GitLab CI
Go to your project **Settings > CI/CD > Variables** and add the same variables. Mark them as **masked** and **protected**.
---
[← Docker Compose](/deployment/docker-compose-setup.md) | [🤖 AI integration →](/ai-integration.md)