GitLab CI S3 Integration and Deployment Orchestration

The integration of GitLab Continuous Integration (CI) with Amazon S3 (Simple Storage Service) represents a cornerstone in modern DevOps for the distribution of static assets and the hosting of Single Page Applications (SPAs). By leveraging GitLab's pipeline orchestration, developers can automate the transition of code from a version control system to a live production environment without manual intervention. This process involves the convergence of containerized build environments, secure identity management via OpenID Connect (OIDC), and the utilize of specific AWS API interactions to synchronize local build artifacts with remote S3 buckets. The architecture typically follows a linear progression from the build stage, where assets are compiled, through a testing phase for quality assurance, and finally to a deployment stage where the assets are pushed to the cloud. This automation not only reduces the risk of human error during deployment but also ensures that the most recent, tested version of an application is consistently available to the end user.

S3 Static Website Hosting Fundamentals

Amazon S3 provides a specific feature known as Static Website Hosting. This functionality allows an S3 bucket to serve web content directly to users via HTTP, effectively bypassing the need for a dedicated web server or compute instance.

The impact of this architecture is a significant reduction in infrastructure overhead. Because the content is stored as objects within a bucket and served directly, there is no need to manage operating system patches, web server configurations (such as Nginx or Apache), or scaling groups. For the user, this results in a highly available and cost-effective hosting solution.

In the broader context of a deployment pipeline, S3 acts as the target destination for the build artifacts. When combined with Amazon CloudFront, a Content Delivery Network (CDN), this setup transforms a simple storage bucket into a global distribution system, ensuring that static assets are cached at edge locations close to the user, thereby reducing latency and increasing the scalability of the application.

AWS CLI Integration for S3 Uploads

One of the most effective methods for implementing GitLab CI uploads to S3 is the use of the official AWS CLI Docker image. Instead of the resource-heavy process of building a custom Docker image and publishing it to a private registry, developers can leverage the official image provided by Amazon.

The use of the amazon/aws-cli image ensures that the environment is pre-configured with the necessary tools to interact with the AWS API. This streamlines the pipeline configuration and reduces the attack surface by using a maintained, official image.

To execute a basic file upload, the pipeline must be configured to use the AWS CLI image with a null entrypoint to allow the GitLab Runner to execute script commands. The following table outlines the required variables for a standard programmatic access setup:

Variable Description Source
S3_BUCKET The unique name of the target S3 bucket AWS Console
AWSACCESSKEY_ID The programmatic access key ID AWS IAM
AWSSECRETACCESS_KEY The secret key associated with the access ID AWS IAM

The implementation of a simple copy command within a GitLab CI script is as follows:

yaml image: name: amazon/aws-cli entrypoint: [""] script: - aws configure set region us-east-1 - touch your-file.txt - aws s3 cp your-file.txt s3://$S3_BUCKET/your-file.txt

The impact of this configuration is the ability to move individual files or directories from the CI runner to the cloud. Contextually, this represents the simplest form of deployment, suitable for small assets or simple scripts, though it lacks the sophistication of a full application sync.

OIDC and Secure Identity Management

For higher security environments, the use of long-lived IAM user credentials is discouraged. Instead, OpenID Connect (OIDC) allows GitLab to receive temporary credentials from AWS, eliminating the need to store sensitive secret keys within GitLab CI variables.

This security model utilizes the AWS Security Token Service (STS) to generate short-lived credentials (typically valid for 3,600 seconds). This reduces the risk of credential leakage; if a token is intercepted, it expires quickly, limiting the window of opportunity for an attacker.

The process involves creating an OIDC role in AWS and configuring the GitLab pipeline to assume this role using a web identity token. The pipeline must define an id_tokens section to pass the required identity claim (the aud claim) to AWS.

The following logic is used to assume the role and export the necessary credentials:

yaml .assume_role: &assume_role - > STS=($(aws sts assume-role-with-web-identity --role-arn ${ROLE_ARN} --role-session-name "GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}" --web-identity-token $ID_TOKEN --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text)) - export AWS_ACCESS_KEY_ID="${STS[0]}" - export AWS_SECRET_ACCESS_KEY="${STS[1]}" - export AWS_SESSION_TOKEN="${STS[2]}"

This mechanism ensures that the runner only possesses the permissions granted to the specific IAM role for the duration of the job. It connects the identity of the GitLab project to the authorization levels of the AWS account, creating a secure, audited link between the CI/CD system and the cloud infrastructure.

Full React Application Deployment Pipeline

A comprehensive deployment pipeline for a React application involves multiple stages: build, test, and deploy. This ensures that only code that has passed unit tests and successfully compiled is pushed to the S3 bucket.

The build stage uses a Node.js environment to compile the source code into a production-ready build/ directory. These files are then saved as artifacts, allowing the subsequent deploy stage to access the compiled assets.

The deploy stage utilizes the amazon/aws-cli:latest image and the OIDC assumption logic to synchronize the build folder with the S3 bucket. The aws s3 sync command is preferred over cp for applications, as it only uploads modified files, optimizing bandwidth and deployment speed.

The complete .gitlab-ci.yml configuration is structured as follows:

```yaml
stages:
- build
- test
- deploy

.assumerole: &assumerole
- >
STS=($(aws sts assume-role-with-web-identity
--role-arn ${ROLEARN}
--role-session-name "GitLabRunner-${CI
PROJECTID}-${CIPIPELINEID}"
--web-identity-token $ID
TOKEN
--duration-seconds 3600
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]'
--output text))
- export AWSACCESSKEYID="${STS[0]}"
- export AWS
SECRETACCESSKEY="${STS[1]}"
- export AWSSESSIONTOKEN="${STS[2]}"

unit test:
image: node:latest
stage: test
before_script:
- npm install
script:
- npm run test:ci
coverage: /All files[^|]\|[^|]\s+([\d.]+)/
artifacts:
paths:
- coverage/
when: always
reports:
junit:
- junit.xml

build artifact:
stage: build
image: node:latest
beforescript:
- npm install
script:
- npm run build
artifacts:
paths:
- build/
when: always
rules:
- if: '$CI
COMMITREFNAME == "main"'
when: always

deploy s3:
stage: deploy
image:
name: amazon/aws-cli:latest
entrypoint:
- '/usr/bin/env'
idtokens:
ID
TOKEN:
aud: reacts3gl
script:
- *assumerole
- aws s3 sync build/ s3://$S3
BUCKET
rules:
- if: '$CICOMMITREF_NAME == "main"'
when: always
```

The impact of this structured approach is a robust delivery cycle. By restricting the deployment to the main branch via rules, the organization prevents untested feature branches from overwriting the production website.

S3 Component and Template Orchestration

For organizations seeking a more modular approach, GitLab CI components and templates can be used to standardize S3 deployments across multiple projects. This prevents the duplication of .gitlab-ci.yml logic and ensures a consistent deployment pattern.

The to-be-continuous/s3 project provides a template that utilizes s3cmd rather than the AWS CLI. This allows for compatibility with any S3-compatible object storage service, not just Amazon AWS. This is particularly useful for users utilizing MinIO or other S3-compatible on-premises solutions.

Users can integrate this as a component using the following configuration:

yaml include: - component: $CI_SERVER_FQDN/to-be-continuous/s3/[email protected] inputs: deploy-files: "website/" staging-disabled: "true" base-bucket-name: "wonder-doc" review-bucket-name: "wonder-doc-review" review-prefix: "$CI_ENVIRONMENT_SLUG" region: "eu-west-0"

For legacy implementations, a project-based include can be used:

yaml include: - project: 'to-be-continuous/s3' ref: '8.2.0' file: '/templates/gitlab-ci-s3.yml' variables: S3_DEPLOY_FILES: "website/" S_STAGING_DISABLED: "true" S3_BASE_BUCKET_NAME: "wonder-doc" S3_REVIEW_BUCKET_NAME: "wonder-doc-review" S3_REVIEW_PREFIX: "$CI_ENVIRONMENT_SLUG" S3_REGION: "eu-west-0"

This modularity allows for the segregation of environments. By utilizing review-prefix and $CI_ENVIRONMENT_SLUG, the system can create unique prefixes within a single bucket for different review environments, enabling stakeholders to preview changes before they are merged into the main branch.

Advanced Secrets Management via HashiCorp Vault

In enterprise environments where secret rotation and centralized management are mandatory, the S3 deployment process can be integrated with a Vault server. This removes secrets entirely from the GitLab environment, delegating management to a dedicated security cluster.

The Vault variant allows the pipeline to retrieve secrets using a specific syntax that calls the Vault API during the execution phase. This is achieved through the gitlab-ci-s3-vault component.

The required configuration parameters for the Vault integration are detailed in the following table:

Input / Variable Description Default Value
TBCVAULTIMAGE The Vault Secrets Provider image registry.gitlab.com/to-be-continuous/tools/vault-secrets-provider:latest
vault-base-url The Vault server base API URL Must be defined
vault-oidc-aud The aud claim for the JWT $CISERVERURL
VAULTROLEID The AppRole RoleID none
VAULTSECRETID The AppRole SecretID none

To retrieve a secret from the Vault server, the following syntax is utilized:

@url@http://vault-secrets-provider/api/secrets/{secret_path}?field={field}

This approach provides a high level of security by ensuring that secrets are only present in the runner's memory during the job execution and are never stored as static variables in the GitLab UI.

Conclusion

The integration of GitLab CI with Amazon S3 provides a scalable and secure pathway for deploying static content. The evolution from basic AWS CLI programmatic access to OIDC-based temporary credentials and centralized Vault management reflects a progression toward higher security and operational maturity. By utilizing the amazon/aws-cli image, developers can implement rapid deployment cycles that are both efficient and reliable. The addition of CI components further enhances this process by providing standardized templates that support multi-environment deployments and S3-compatible storage. Ultimately, the synergy between GitLab's orchestration and S3's static hosting capabilities allows for the creation of a high-performance delivery pipeline that minimizes infrastructure overhead while maximizing deployment speed and security.

Sources

  1. GitLab CI to Amazon S3
  2. GitLab CI template for S3
  3. How to deploy React to Amazon S3

Related Posts