Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
FREEDOM251

Freedom251 › Blog › Buildah

How to Build Docker Images and Host a Private Registry with GitLab

By Freedom251 Team · · 12 min read

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use GitLab CI to build an image, authenticate to GitLab’s private Container Registry, push an immutable tag based on the commit SHA, test that exact image from a clean job, and promote it to latest only during a controlled release. This pattern prevents concurrent pipelines from overwriting one another and gives deployments a traceable image.

The registry is GitLab’s private store for Docker and OCI images. A typical implementation uses a Docker CLI job image with a Docker-in-Docker service, although Buildah or Podman can replace DinD when runner policy makes privileged Docker unavailable.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What the finished GitLab setup does

A reliable pipeline separates four concerns:

  • Build: turns the repository’s Dockerfile and application source into an image.
  • Test: pulls the image by an immutable tag and runs tests against the same artifact that can later be deployed.
  • Release: promotes the tested image to a human-friendly release tag such as latest, but only under explicit rules.
  • Deploy: updates an environment from the approved release tag and records the deployment in GitLab’s environment history.

GitLab’s documented requirement is straightforward: “Before you can build and push container images, you must authenticate with the container registry.” Authentication therefore happens before both docker build and docker push, not as a cleanup step afterward.

The project variable $CI_REGISTRY_IMAGE expands to the registry repository associated with the project. A resulting image might therefore be addressed as $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA. The exact hostname and namespace are supplied by GitLab for the project; do not hard-code a different repository path unless you intentionally publish elsewhere.

#1 Best Overall
Sale
TOZO A1 Wireless Earbuds Bluetooth 5.3 Light Weight in Ear Headphones | IPX5 Waterproof,2 Mics for AI Calls,Immersive Premium Sound Bass Headset with Charging Case,32 EQ via App Black
  • [Ultra-Lightweight Ear Buds Designed for Small Ears] Each earbud weighs only 3.7g and features a compact, ergonomic in-ear design made especially for small ears. Secure, low-profile, and comfortable for workouts, all-day wear or overnight sleeping.The ultra-slim profile sits flush in your ear with no hard edges poking or rubbing, so you can even sleep on your side without discomfort.
  • [Immersive Stereo Sound with TOZO OrigX Technology] TOZO OrigX tuning delivers clear vocals, balanced mids, and natural stereo sound for music, podcasts, and videos.
  • [Long Battery Life for Daily Use] Get up to 7 hours of playtime on a single charge, with up to 32 hours total using the charging case—ideal for workdays, commuting, and extended listening sessions.
  • [Bluetooth 5.3 & Stable Connection] Bluetooth 5.3 provides fast pairing, stable wireless performance, and reduced dropouts as you move around home or office.
  • [Deep Bass with Clear Vocals] High-performance drivers produce punchy bass while keeping vocals clean and detailed for everyday listening.

Prerequisites and decisions to make first

Repository contents

Commit a tested Dockerfile and the files it needs into the project. Keep the build context narrow with a .dockerignore; excluding local caches, credentials, build output and version-control metadata reduces transfer time and avoids copying secrets into layers.

Runner capabilities

Your GitLab Runner must be able to execute the selected build engine. Docker-in-Docker (DinD) generally requires a runner configuration that permits the service to start with the required privileges. A runner using a host Docker socket avoids starting a nested daemon, but the socket gives jobs control over the host daemon and must be evaluated against the runner’s trust boundary.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If privileged DinD is not acceptable, choose Buildah or Podman and confirm that the executor, storage driver and policy permit rootless or otherwise approved image builds.

Registry and certificate plan

For GitLab’s hosted registry, use the registry address exposed in the project’s predefined variables. For a self-managed GitLab server, verify that the registry endpoint is reachable from the runner. When the GitLab server or registry uses a self-signed certificate, install the issuing CA in the runner container and in any Docker daemon or build tool that connects to it. A missing CA trust chain commonly appears as a TLS or certificate error during login, pull or push.

Write and verify the Dockerfile locally

Start with a Dockerfile that can be built outside CI. A minimal example is:

FROM alpine:3.20
WORKDIR /app
COPY . .
RUN ./build.sh
CMD ["./start.sh"]

Use the language runtime and base-image version appropriate for your application; the example only illustrates structure. Build and run it locally before involving GitLab:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
Smart Watch for Men Women Compatible with iPhone Samsung Android Phone 1.69 inch Full Touch Screen IP68 Waterproof Bluetooh Fitness Tracker Heart Rate/Sleep Monitor (Black)
  • 【IP68 Waterproof Multifunctinal Smart Watch】Torjalph fitness activity tracker smart watch is IP68 waterproof technology ,you can wear it when swimming,handwashing,it's rain-proof which means that you perform multiple exercise modes outside without worrying about sweat or rain corroding your fitness smart watch,and this watch equipped with a 230 mAh large capacity battery magnetic charging ,and this sports activity tracker watch can be used 5-7 days If fully charged.
  • 【Larger 1.83'' Full Screen Watch & Multi-dial Selection】1.83 inch full HD color touch screen smart watch for men / women provides excellent 240*284 resolution image quality ,This Bluetooth smart watch offers a better touch experience and remarkable immersion and ,more than 200 watch faces for you to choose from App"Da Fit ", you can change the dial style according to your mood to wear keeping the world on your wrist
  • 【Accurate Activity Tracker Smart Watch 】sports watch activity tracker uses sweat and dust resistant breathable silicone strap, our Fitness Tracker Smart Watch for Android iOS Phones supports 12 sports modes,walking,running,bicycle,swimming,badminton,basketball,football,rope skipping. This bluetooth calls smart watch for men women is lightweight and stylish, bringing more convenient and relaxing sports experience while also providing more protection.
  • 【Your 24 Hour Personal Daily Health Monitor】Fitness watch for women men full touch screen IP67 waterproof bluetooth smartwatch for Android iOS Phones with heart rate blood pressure monitor multifunction Andriod smart watch with steps counter sleep monitor sports fitness watch, monitoring blood oxygen saturation allows you to understand your physical condition in real time to track fitness progress ,to know your health status in time.
  • 【More Functions Bluetooth Call Smart Watch】This fitness activity smart watch full screen for iPhone and Android phones includes a bunch of new features including:The bluetooth smart watch can hang up the phone, receive and display WhatsAPP /Text /Facebook /Twitter /Messenger reminder.Support sedentary reminder, alarm clock, stopwatch, timer,weather information,music control, remote camera control compatible with Android 5.0 and above or iOS 9.0 and above smartphones.
docker build --pull -t example-app:local .
docker run --rm -p 8080:8080 example-app:local

The --pull option asks Docker to check for a newer version of the referenced base image. It does not make a build reproducible by itself: pin base images by digest when your supply-chain policy requires byte-for-byte repeatability, and record how those digests are updated.

Make the container’s listening port, health endpoint, required environment variables and startup command explicit. A local build that depends on an uncommitted file or a developer-only credential will fail in a clean runner, which is preferable to discovering the dependency after deployment.

Build and push with Docker-in-Docker

The following job follows GitLab’s documented pattern. It uses a Docker CLI image and a matching Docker-in-Docker service, logs in with predefined CI credentials, builds an immutable commit tag and pushes that exact tag.

build:
  image: docker:24.0.5-cli
  services:
    - docker:24.0.5-dind
  variables:
    IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
    - docker build --pull -t "$IMAGE_TAG" .
    - docker push "$IMAGE_TAG"

How each line works

  • image supplies the Docker client used by the job.
  • services starts a Docker daemon reachable by the client. Keep the client and service versions aligned unless you have tested the combination.
  • IMAGE_TAG combines the project registry path with $CI_COMMIT_SHA, which identifies the source revision that triggered the pipeline.
  • docker login reads the password from standard input, avoiding a secret on the command line. GitLab provides CI_REGISTRY_USER and CI_REGISTRY_PASSWORD for the job.
  • docker build --pull refreshes base-image metadata before building.
  • docker push publishes the exact tag created by the build.

DinD connection settings

The job and service must agree on the service hostname and Docker connection settings. In a runner configuration that exposes the daemon as the service named docker, jobs commonly set a Docker host such as tcp://docker:2375 and configure the TLS directory consistently with the service. Whether TLS is enabled, which port is used and whether a certificate directory is mounted depends on the runner’s DinD configuration; copy the settings required by that runner rather than mixing secure and non-secure examples.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If the client reports that it cannot reach Docker, inspect the service logs first, then check the hostname, port, certificate variables and whether the runner permits the service to start.

Use immutable tags as the pipeline contract

Tagging determines whether a deployment can be reproduced. Use the commit SHA for artifacts that represent one source revision:

Rank #3
New Bee Guitar Cable 10ft Electric Instrument Bass AMP Cord | 6.35mm Jack, Durable Design, Stage and Home Studio Use, Low Noise Shielded Electric Cord for Bass, Keyboard, Amp, Mixer
  • [Low Noise High Fidelity] New bee guitar cord with 22AWG oxygen-free-copper center conductors provides enhanced signal transfer, the PVC insulation material and the high-density stranded copper double shield provide noise rejection for quiet operation
  • [Ideal for Choice] New bee 1/4 straight and right angle metal connectors, ensures a secure connection in any instrument, pedal, or amp jack. L shape great fro on stage guitar stands
  • [Wide Compatible] Suitable for mixer, power amplifier, microphone, effector, decoder, equalizer, electoronic organ, electric guitar, electric bass and other electronic equipments. Widely used in the installation of smart home, karaoke speaker, professional stage audio, etc
  • [Easy Disassemble] Making incidental repairs quick and easy! Very nice, flexible cable
  • [Dont Hesite to Order] 24 hours customer services and professional technology team are standing by
$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

Every pipeline gets a distinct tag, so a later build cannot silently replace an image that an earlier pipeline used. Downstream jobs should receive the tag as a variable or artifact rather than reconstructing it from a branch name.

Branch and review builds

For a branch-oriented review image, use $CI_COMMIT_REF_SLUG instead of the raw branch name. Git branch names can contain forward slashes, while Docker image tags cannot; GitLab’s slug variable converts the reference to a tag-safe form. Branch tags are convenient for reviewers but are mutable: a subsequent commit on the branch can replace the same tag. Use them for temporary environments, not for an audit-grade release.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Release tags

Human-readable tags such as latest or a version label should be assigned only after testing. Never have every concurrent build push directly to latest; the last job to finish would determine what that tag means, regardless of which commit was tested.

Pull the image explicitly in downstream jobs

A runner can have a stale local cache, and different jobs may run on different machines. A test job should therefore pull the image before running it:

test_image:
  image: docker:24.0.5-cli
  services:
    - docker:24.0.5-dind
  variables:
    IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
    - docker pull "$IMAGE_TAG"
    - docker run --rm "$IMAGE_TAG" ./run-tests.sh

Adapt the command to the image’s actual test entry point. If tests require a database or other service, declare those services and networking explicitly. The important property is that the job tests the registry copy identified by the commit tag, not an unpushed local layer left by another job.

Separate build, test, release and deployment stages

A multi-stage pipeline makes promotion rules visible. The following outline builds and tests every commit, then allows only the default branch to promote the tested image and deploy it with an approval gate:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
BSIMB 15.6" Digital Family Calendar & Chore Chart, No Subscription Fees
  • Digital Family Calendar: BSIMB digital calendar wall touch screen puts an end to your chaotic life. Designed for busy families, the digital calendar keeps you organised and in step with your family. Assign different colours and tasks to each family member to make family scheduling easier and more intuitive.
  • Easy Setup and Auto-Sync: Plug in, connect to WLAN and auto-syn your electric calendar in minutes. Digital wall calendar compatible with Google calendar, iCloud calendar, Outlook calendar, Cozi calendar and Yahoo calendar. Add events directly on the device or through the ecalendar app. Electronic calendar stays current whether you're at work or traveling—no manual updates needed.
  • All-In-One Home Assistant: Smart calendar builds habits and encourage independence with personalised chores. Display daily meal plan to reduce the daily ‘What's for dinner?’ stress. To-do lists keep track of everything you want. Turning chores into valuable achievements with stars and rewards, making life easier and more enjoyable.
  • Manage Anytime Anywhere: BSIMB electronic calendar for family stay connected with the powerful ecalendar app. Access and manage your digital wall calendar anytime, anywhere with free features like stars & rewards, photo screensaver, chore charts, meals plans, to-do lists etc. Electronic planner paid features like Magic Import tailor advanced tools for users with special needs.
  • Stay in Touch with Loved Ones: Share photos or videos from your phone to electronic wall calendar with app, instantly update your life moment, and keep in touch with your family, especially those who are far away; you can also surprise your smart calendar digital wall by sending him a wish card; it's also great for keeping tech-illiterate elders in touch with the youngsters.
stages:
  - build
  - test
  - release
  - deploy

variables:
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

build:
  stage: build
  image: docker:24.0.5-cli
  services:
    - docker:24.0.5-dind
  script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
    - docker build --pull -t "$IMAGE_TAG" .
    - docker push "$IMAGE_TAG"

image_test:
  stage: test
  image: docker:24.0.5-cli
  services:
    - docker:24.0.5-dind
  script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
    - docker pull "$IMAGE_TAG"
    - docker run --rm "$IMAGE_TAG" ./run-tests.sh

release:
  stage: release
  image: docker:24.0.5-cli
  services:
    - docker:24.0.5-dind
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
  script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
    - docker pull "$IMAGE_TAG"
    - docker tag "$IMAGE_TAG" "$CI_REGISTRY_IMAGE:latest"
    - docker push "$CI_REGISTRY_IMAGE:latest"

deploy_production:
  stage: deploy
  needs: [release]
  environment:
    name: production
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      when: manual
  script:
    - ./deploy.sh "$CI_REGISTRY_IMAGE:latest"

The release job pulls the tested commit image before retagging it. That extra pull ensures the release job is promoting an image that exists in the registry, not relying on a local daemon cache. The deployment job’s environment: production declaration lets GitLab record deployment history; when: manual adds an approval point. You can use a version tag instead of latest when your release process defines one.

Choose the right credentials for each caller

Do not put a long-lived password or token directly in .gitlab-ci.yml. Use GitLab’s protected and masked CI/CD variable controls, and grant each identity only the access it needs.

Use case Credential category How it is used Important control
GitLab CI/CD build and push CI/CD job token and predefined registry variables Log in with CI_REGISTRY_USER and CI_REGISTRY_PASSWORD before pulling or pushing. Keep jobs and release rules scoped to the projects and branches that should publish images.
Local developer Docker client Personal access token or group access token Run docker login against the registry using the token as the password. Use an appropriate scope and revoke the token when it is no longer needed.
External read-only consumer Deploy token Provide the consumer with registry credentials that can pull but do not publish. Prefer read-only access and rotate the token according to your operations policy.
Runner pulling a private job image Runner registry authentication Configure the runner to authenticate, then reference a fully qualified image such as my.registry.tld:5000/namespace/image:tag. Ensure the runner trusts the registry CA and can resolve the registry hostname.

For a local login, the command shape is:

docker login REGISTRY_HOST

When prompted, enter the username and token appropriate to that registry. Avoid putting the token in shell history or a committed script.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Handle private certificates and runner trust

A private registry can be healthy while every CI job fails because the runner does not trust its certificate authority. Install the CA certificate in the runner container or host image that performs the connection, and update the trust store before starting Docker, Buildah or Podman. The Docker daemon used by DinD may need the CA separately from the job container.

  • Login fails with an x509 or unknown-authority error: add the issuing CA to the client and daemon trust stores, then restart the component that caches trust settings.
  • Pull works locally but not in CI: compare the local machine’s CA bundle, DNS resolution and proxy settings with the runner.
  • Only the runner’s job image fails: check whether the job image contains the CA; installing it on the GitLab host alone does not automatically install it inside a container.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Buildah and Podman when DinD is not a fit

Buildah and Podman provide OCI-compatible build and push workflows and can reduce reliance on a privileged nested Docker daemon. They are not drop-in fixes for every runner: storage drivers, rootless permissions, networking and registry certificates still need configuration.

Build engine Typical login/build/push flow Strength Trade-off to evaluate
Docker CLI with DinD docker login, docker build, docker push Broad Dockerfile and tooling familiarity. Often needs a privileged service and careful daemon/TLS setup.
Buildah buildah login, buildah bud, buildah push Daemonless, OCI-native builds with flexible isolation. Rootless storage and Dockerfile compatibility must be tested on the chosen executor.
Podman podman login, podman build, podman push Docker-compatible command model without a central daemon. Check networking, authentication-file paths and runner policy, especially in rootless mode.

The credential principle is unchanged: authenticate before pulling or pushing, use the same immutable commit tag, and make downstream jobs pull the registry copy. Select the engine according to runner executor, privilege policy and Dockerfile compatibility rather than treating one engine as universally safer.

Secure the pipeline and the image

Protect secrets

  • Store deployment tokens, private keys and registry credentials in protected or masked CI/CD variables.
  • Do not pass secrets as Docker build arguments; build arguments can be recorded in image history or layer metadata. Use a supported secret-mount mechanism when the builder and Dockerfile require a secret.
  • Review logs for commands that might echo tokens. The password-stdin login pattern keeps the token out of the command argument list.

Limit publishing rights

Allow ordinary branches to build and test, but restrict release jobs to the default or main branch through rules, protected branches or protected environments. Give external consumers read-only credentials and keep write credentials inside CI.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep base images current without losing traceability

docker build --pull checks for a newer base image during each build. Combine that freshness check with a deliberate update process: review the resulting digest, run the image tests, and retain the commit tag that identifies exactly what was released.

Review runner isolation

A runner that mounts /var/run/docker.sock exposes the host Docker daemon to jobs. This can be acceptable for a tightly controlled runner but is a significant trust-boundary decision. Separate runners by trust level when untrusted contributions and production publishing should not share the same daemon or credentials.

Troubleshoot the common failures

“unauthorized” during push

  • Confirm that docker login runs before docker push and uses the same registry hostname as the image tag.
  • Print the image name without secrets and verify that it begins with $CI_REGISTRY_IMAGE.
  • Check that the job identity has permission to publish to that project’s registry.
  • For a local client, replace an expired personal or group token and ensure the token’s scope permits the requested operation.

“manifest unknown” or an image that cannot be pulled

  • Compare the tag in the producer job with the tag in the consumer job; a missing or altered commit SHA is the most common cause.
  • Make the consumer run docker pull "$IMAGE_TAG" explicitly.
  • Check whether the build failed before the push step or whether a cleanup job removed the image.

Branch tag rejected by Docker

Raw branch names may contain / and other characters that are invalid in image tags. Use $CI_COMMIT_REF_SLUG for review images, or use the commit SHA for immutable artifacts.

DinD client cannot connect

  • Verify that the service is named as the hostname used by the client.
  • Check the Docker host, port and TLS certificate variables on both job and service.
  • Read service startup logs for privilege, storage-driver or certificate errors.
  • Confirm the runner configuration permits the required service mode.

TLS handshake or certificate errors

Install the private CA in every component that connects: the job container, the DinD daemon and any deployment tool. Confirm that the certificate’s hostname matches the registry address used in docker login.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Release points to the wrong commit

Do not build directly to latest in concurrent jobs. Have the release job pull the tested $CI_COMMIT_SHA image, retag it and push the release tag under a protected branch rule. If two releases can run at once, add an environment or pipeline-level serialization policy so approvals cannot promote an unexpected commit.

Operational checklist

  1. Build and run the Dockerfile locally with a clean context.
  2. Verify the runner executor, DinD privilege model or Buildah/Podman policy.
  3. Confirm registry DNS, firewall access and private-CA trust from the runner.
  4. Log in with CI_REGISTRY_USER and CI_REGISTRY_PASSWORD before every registry operation.
  5. Build with --pull and tag the image with $CI_COMMIT_SHA.
  6. Push that exact immutable tag.
  7. Pull the tag in downstream tests before running the container.
  8. Promote to latest or a release tag only after tests pass and only under protected rules.
  9. Deploy through a named GitLab environment, using a manual approval when production requires it.
  10. Rotate personal, group and deploy tokens; review masked and protected variable settings.
  11. Record which runner trust boundary and build engine produced the artifact.

How to choose an implementation

Use Docker-in-Docker when your team already operates privileged Docker runners and needs the familiar Docker CLI. Choose Buildah or Podman when daemonless or rootless operation better matches security policy, provided your Dockerfile and executor have been validated. Regardless of engine, keep the credential, tag and promotion rules consistent.

The most important design choice is not the command that performs the build; it is the artifact contract. A commit-SHA tag identifies one source revision, an explicit pull verifies that downstream jobs use the registry copy, and a controlled promotion gives latest a deliberate meaning. GitLab then supplies private storage, access control and deployment history around that contract.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

BuildahCI/CDContainer RegistryDevOpsDockerGitLabPodman

Leave a Reply

Your email address will not be published. Required fields are marked *

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.