Publishing
GitLab CI builds this site and syncs documentation/build to an S3 bucket. The pipeline is .gitlab-ci.yml at the repository root.
A pipeline runs when documentation/** or .gitlab-ci.yml changes:
- Merge requests and other branches build the site only.
- The default branch (
main) builds the site, then syncs it to S3.
The deploy job uses aws s3 sync --delete. Use a bucket that contains only this site. Objects that are no longer in the build are removed.
GitLab CI/CD variables
Set these in Settings → CI/CD → Variables. Mark secrets as masked, and mark every variable below as protected so they are only available on protected branches.
| Variable | Required | Purpose |
|---|---|---|
DOCS_S3_BUCKET | Yes | Destination bucket name |
AWS_DEFAULT_REGION | Yes | Bucket region |
AWS_ROLE_ARN | Yes, for OIDC | IAM role GitLab assumes |
AWS_ACCESS_KEY_ID | Alternative to the role | Static access key |
AWS_SECRET_ACCESS_KEY | Alternative to the role | Static secret key |
DOCS_URL | Recommended | Public site URL baked into canonical links, for example https://docs.example.com |
DOCS_BASE_URL | No | Path prefix. Defaults to /. Include leading and trailing slashes |
CLOUDFRONT_DISTRIBUTION_ID | No | When set, the job invalidates /* after the sync |
AWS_OIDC_AUDIENCE | No | OIDC audience. Defaults to https://gitlab.com |
Prefer AWS_ROLE_ARN. The deploy job requests an OIDC token and calls sts:AssumeRoleWithWebIdentity. Static keys are used only when AWS_ROLE_ARN is unset.
IAM role
Create an IAM OIDC provider for https://gitlab.com if the account does not have one. Trust this project on main:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/gitlab.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"gitlab.com:aud": "https://gitlab.com"
},
"StringLike": {
"gitlab.com:sub": "project_path:zontally/zontallyirm:ref_type:branch:ref:main"
}
}
}
]
}
Allow the role to update the bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::<BUCKET_NAME>"
},
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::<BUCKET_NAME>/*"
}
]
}
Add cloudfront:CreateInvalidation on the distribution when CLOUDFRONT_DISTRIBUTION_ID is set.
Bucket hosting
Pages are built with trailingSlash: true, so each page is a directory containing index.html.
Either of these hosting setups works:
- S3 static website hosting, with index document
index.htmland error document404.html. - CloudFront in front of the bucket (origin access control), with the default root object
index.html.
Set DOCS_URL to the URL readers use. The next default-branch pipeline republishes the site with that URL in the canonical links.