Centralizing Dockerfiles in a single repository can eliminate duplication and simplify maintenance, but GitLab’s default behaviors often complicate this approach. Many teams face a critical challenge: how to reuse a Dockerfile stored in one repository across multiple projects without copying files or compromising security. The solution lies in leveraging GitLab’s CI job token and repository files API, enabling secure, runtime access to remote files during pipeline execution.
The Pitfalls of Standard GitLab CI Includes
Teams often attempt to solve this problem using GitLab’s include directive, which imports CI/CD configuration from another project. While this approach works for YAML snippets, it does not grant access to files like Dockerfiles. For example, a pipeline configuration such as:
include:
- project: "kelvyn-labs/cicd-template"
ref: master
file: "/frontend/gitlab-ci.yml"may successfully import CI logic but fails when the pipeline tries to reference a Dockerfile located in another repository. Commands like docker build -f frontend/Dockerfile . often result in file-not-found errors, as the pipeline executes within the context of the calling repository, not the template repository.
Why Common Workarounds Fall Short
Several alternative solutions are commonly proposed, but each introduces significant drawbacks:
- Copying the Dockerfile into every repository eliminates the benefits of centralization and creates maintenance overhead.
- Using Git submodules adds complexity and can introduce dependency conflicts.
- Assuming
includegrants file access is a fundamental misunderstanding of GitLab’s design.
A Secure Solution: The CI Job Token Approach
GitLab provides a built-in mechanism to securely fetch files from another repository during a pipeline run: the CI_JOB_TOKEN. This token is automatically generated for each job and is valid only for the duration of that job. It can be used to authenticate API requests to fetch files from other projects.
The workflow is straightforward:
- The pipeline job in the calling repository requests the Dockerfile.
- The request is authenticated using
CI_JOB_TOKEN. - The GitLab API retrieves the Dockerfile from the template repository.
- The Dockerfile is saved locally and used in the build process.
This method ensures centralized control, eliminates duplication, and maintains security through token-based access.
Step-by-Step Implementation Guide
Step 1: Configure Cross-Project Access
Before fetching files, the template repository must explicitly allow access from the calling repository. This is configured in the template project’s settings:
- Navigate to
Settings → CI/CD → Job token permissions. - Under
Fine-grained permissions, add the project ID or name of the repository that will fetch the Dockerfile. - Save the changes to enable token-based access.
Step 2: Fetch the Dockerfile Using the API
In the calling repository’s .gitlab-ci.yml, define a job to fetch the Dockerfile from the template repository. Use the following template, adjusting the variables as needed:
.fetch-dockerfile:
variables:
CICD_TEMPLATE_PROJECT_ID: "81568045" # Replace with your template project ID
CICD_TEMPLATE_REF: "master" # Branch, tag, or commit SHA
CICD_TEMPLATE_DOCKERFILE_PATH: "frontend%2FDockerfile" # URL-encoded path
script:
- echo "Downloading Dockerfile from template repository..."
- 'curl --fail --location --header "JOB-TOKEN: $CI_JOB_TOKEN"
"
-o Dockerfile'Key considerations:
- The file path must be URL-encoded (e.g.,
frontend/Dockerfilebecomesfrontend%2FDockerfile). - Use the project ID for simplicity and reliability.
- The token is automatically injected as
CI_JOB_TOKENand is valid only during the job.
Step 3: Build and Push the Docker Image
With the Dockerfile now available in the calling repository’s workspace, proceed with the build process:
docker buildx build \
--file Dockerfile \
--tag $IMAGE_TAG \
--push .This command builds the image using the fetched Dockerfile and pushes it to a container registry.
Best Practices for Long-Term Maintenance
Pin Versions Instead of Using Master
To avoid unexpected changes, reference a specific tag or commit rather than the master branch:
variables:
CICD_TEMPLATE_REF: "v1.2.0"Make the Configuration Reusable
Centralize the configuration by defining reusable variables:
variables:
CICD_TEMPLATE_PROJECT_ID: "81568045"
CICD_TEMPLATE_DOCKERFILE: "frontend%2FDockerfile"Know When to Use Git Clone Instead
This approach works best for single-file reuse. If your Dockerfile depends on multiple files (e.g., scripts, configs), consider cloning the entire repository:
git clone --depth 1 --branch $CICD_TEMPLATE_REF Why This Method Stands Out
Adopting this pattern delivers multiple advantages:
- Centralized Control: A single team manages build logic, reducing drift and inconsistencies.
- No Duplication: Eliminates the need to copy-paste Dockerfiles across repositories.
- Security: Access is controlled via allowlists and scoped tokens.
- Flexibility: Applicable to front-end, back-end, and any service requiring standardized builds.
This method is already in use by engineering teams building internal CI/CD platforms, standardized pipelines, and centralized build systems. If you’ve ever faced the frustration of pipelines failing due to missing files, this is the missing piece you’ve been looking for.
Looking Ahead: Treat CI/CD as a Platform
Modern DevOps practices demand treating CI/CD pipelines as first-class platforms. By centralizing build artifacts like Dockerfiles, teams can enforce consistency, reduce toil, and focus on delivering value rather than managing duplicated configurations. The next time you consider copying a Dockerfile into another repository, pause and evaluate whether this token-based approach could streamline your workflow and improve maintainability.
AI summary
Learn how to securely fetch and reuse Dockerfiles from a central GitLab repository using CI job tokens for scalable, duplication-free CI/CD pipelines.