---
title: "How to Pull Images in Docker and K8s"
description: "A Complete Guide to Pulling Private Images from GitLab Registry with Docker and Kubernetes, Including Deploy Token Setup, ImagePullSecret Creation, and Key Security Best Practices."
canonical_url: "https://blog.markkulab.net/en/post/docker-k8s-pull-image-guide"
author: "Mark Ku"
author_url: "https://blog.markkulab.net/en/author/mark-ku"
site: "Mark Ku's Tech Notes"
date_published: "2025-09-03 02:00:00 +0800"
category: "DevOps"
tags: ["docker", "kubernetes", "gitlab", "cicd", "image pull", "registry", "authentication", "devops"]
language: "en"
license: "CC BY 4.0"
license_url: "https://creativecommons.org/licenses/by/4.0/"
attribution: "when reusing or quoting, credit the author and link back to the original"
---

# How to Pull Images in Docker and K8s

## 1. Overview

In containerized deployments, securely and reliably pulling private images is a common requirement. This article will concisely explain two things:
- How to pull an image from a GitLab Registry with Docker on a local machine or VM
- How to pull the same image within a Kubernetes cluster using an ImagePullSecret

> **TL;DR**
>- Docker: First, use a Deploy Token to `docker login`, then `docker pull`
>- Kubernetes: Create a Secret of type `docker-registry` and reference it in your `imagePullSecrets`

## 2. How Docker Pulls a GitLab Image

### 2.1 Obtain a GitLab Deploy Token

Go to your project → Settings → Repository → Deploy tokens

Select the required permissions:

- **read_registry** → To pull an image
- **write_registry** → To push an image

P.S. If you have Group permissions, you can also create a Group Access Token, which allows you to pull images across multiple projects.

Take note of the username and password provided by GitLab.

### 2.2 Docker Authentication and Pulling (Local/VM)

```bash
# 清除舊認證（可選）
docker logout registry.abc.com

# 使用 Deploy Token 登入（建議用 --password-stdin）
echo "<deploy_token_password>" | docker login registry.abc.com -u <deploy_token_username> --password-stdin

# 拉取測試
docker pull registry.abc.com/kong/kong-api-gateway/main:70368
```

Why is it recommended to use `--password-stdin`?

- Prevents the password from appearing in your command-line history (e.g., `~/.bash_history`, PowerShell history).
- Prevents the password from being exposed in the system process list (parameters are visible in Linux with `ps` and Windows with `Get-CimInstance Win32_Process`).
- Ideal for non-interactive CI/CD environments, and is more secure when paired with masked environment variables (masked secrets).
- The official recommendation is to avoid using plaintext parameters with `--password`; using standard input is more secure and auditable.

Example (a more secure, one-time input):

```bash
# Bash / Linux / macOS：建議用 printf 避免 echo 行為差異
printf "%s" "$DEPLOY_TOKEN" | docker login registry.abc.com -u "$DEPLOY_USER" --password-stdin
```

```powershell
# Windows PowerShell
$Env:DEPLOY_TOKEN | docker login registry.abc.com -u $Env:DEPLOY_USER --password-stdin
```

### 2.3 For Registries Without HTTPS (Insecure Registry)

If your registry does not use HTTPS, you need to configure `insecure-registries` in the Docker Daemon settings:

```bash
# 編輯 Docker daemon 配置
sudo nano /etc/docker/daemon.json

# 加入以下內容
{
  "insecure-registries": ["registry.abc.com:5000", "192.168.1.100:5000"]
}

# 重啟 Docker 服務
sudo systemctl restart docker

# 或者重啟 Docker Desktop (Windows/Mac)
```

**Notes:**
- Using an HTTP registry is not recommended for production environments.
- If you are using a self-hosted GitLab registry, it's recommended to configure an SSL certificate.
- You can use HTTP in development environments, but be mindful of the security implications.

## 3. How to Pull Images in Kubernetes

### 3.1 Create an ImagePullSecret

Create a Secret of type `docker-registry` in the namespace where you plan to deploy:

```bash
# 建立 Docker Registry Secret
kubectl create secret docker-registry kong-api-gateway-secret \
  --docker-server=registry.abc.com \
  --docker-username=<deploy_token_username> \
  --docker-password=<deploy_token_password> \
  --docker-email=none \
  -n <your-namespace>
```

### 3.2 Reference it in a Deployment

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kong-api-gateway
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kong-api-gateway
  template:
    metadata:
      labels:
        app: kong-api-gateway
    spec:
      containers:
        - name: kong
          image: registry.abc.com/kong/kong-api-gateway/main:70368
          ports:
            - containerPort: 8000
      imagePullSecrets:
        - name: kong-api-gateway-secret
```

### 3.3 Verify the Functionality

```bash
# 檢查 Secret 是否建立成功（命名空間必須正確）
kubectl get secret kong-api-gateway-secret -n <your-namespace> -o yaml

# 重新建立 Secret
kubectl delete secret kong-api-gateway-secret -n <your-namespace>
kubectl create secret docker-registry kong-api-gateway-secret \
  --docker-server=registry.abc.com \
  --docker-username=<deploy_token_username> \
  --docker-password=<deploy_token_password> \
  --docker-email=none \
  -n <your-namespace>
```

Additional Notes
* Secrets are namespace-scoped and must be in the same namespace as the workload.
* To allow all Pods in a namespace to automatically pull private images, you can add the Secret to the default ServiceAccount for that namespace.
* Secrets cannot be shared across namespaces. If needed, create or sync them in each target namespace individually.

```bash
# 檢查 Secret 是否存在於指定命名空間
kubectl get secret kong-api-gateway-secret -n <your-namespace>

# 將 Secret 掛到該命名空間的 default ServiceAccount
kubectl patch serviceaccount default -n <your-namespace> -p '{"imagePullSecrets":[{"name":"kong-api-gateway-secret"}]}'
```

## 4. Addendum: GitLab Key Security

When using keys or passwords in GitLab, keep the following points in mind to prevent leaks:

### 4.1 Masked Variables

* **Functionality**: When enabled, the variable's value will be replaced with `[MASKED]` in CI logs, hiding its actual content.
* **Limitations**: Must conform to GitLab's rules (at least 8 characters, alphanumeric + some symbols). It does not support spaces or newlines.
* **Recommendation**: If your secret contains newlines or special characters, use a **File variable** or Base64 encode it first.
* **More Secure**: Also set it as **Protected** to restrict its use to protected branches/tags only.

### 4.2 File-Type Variables (Recommended for Multi-line Keys)

Suitable for SSH private keys, Kubeconfig files, JSON credentials, etc.

1. When setting up the variable in GitLab, select the type **File** and paste the entire content directly (including newlines).
2. In the pipeline, the variable will resolve to a **file path**, not a string.

Example:

```bash
chmod 600 "$SSH_PRIVATE_KEY"
GIT_SSH_COMMAND="ssh -i $SSH_PRIVATE_KEY -o StrictHostKeyChecking=no" \
  git ls-remote git@your.gitlab.com:group/project.git
```

### 4.3 Avoid Printing Secrets

* Use **standard input** to pass passwords to avoid them appearing in command-line or shell history.
* Turn off `set -x` in sections where secrets are handled.

Example:

```bash
printf "%s" "$DEPLOY_TOKEN" | docker login registry.abc.com -u "$DEPLOY_USER" --password-stdin
```

### 4.4 Principle of Least Privilege

* Grant only the necessary permissions (e.g., a Deploy Token only needs `read_registry`).
* Scope variables to specific branches or environments.
* Tokens should have an **expiration date** and be rotated regularly.

### 4.5 Password Security Best Practices

* `--password-stdin`: Prevents passwords from appearing in the command line.
* **File/Masked/Protected variables**: Prevent leaks in CI logs.
* **K8s ImagePullSecret**: Avoid hardcoding credentials in YAML files.
* **Additional Recommendations**: `unset` after use, delete temporary files, enable cleanup on the Runner, and enable Secret encryption in Kubernetes.

### 4.6 Common Mistakes (What Not to Do)

* Hardcoding passwords in `docker login -p`, Dockerfiles, YAML, or .env files.
* Pasting full error messages or logs containing secrets into MRs, issues, or chat.
* Reusing the same secret for different purposes across namespaces.

---

## About this article and its author

Originally published on [Mark Ku's Tech Notes](https://blog.markkulab.net/en/post/docker-k8s-pull-image-guide)

License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) — when reusing or quoting, credit the author and link back to the original

### About the author

**[Mark Ku](https://blog.markkulab.net/en/author/mark-ku)** — Software Solution Provider

- 10+ years senior software engineer, now an AI Builder
- Focused on large-platform architecture — North-American e-commerce, AI SaaS subscription billing
- Combining AI Agents and automation to build evolvable product foundations

### Free tools built by the author

All of these are free to use:

- [Free PDF Sign Tool](https://blog.markkulab.net/en/tools/pdf-sign): Online PDF sign tool — draw, type, or upload a signature, then drag, resize, and download. Everything runs in your browser; nothing is uploaded.
- [VS Code Refactory](https://blog.markkulab.net/en/tools/refactory): Refactory is a VS Code refactoring extension: 34 actions plus a 37-rule code-smell inspection layer with a Code Health dashboard, across 18 languages, backed by 534 tests. It learns your repo's conventions: where interfaces live, where DI is registered, whether 'use client' belongs. It ranks files by git churn × complexity so you know what to fix first, and hands any smell to the Claude Code already on your machine. Free to use, and your source never leaves your computer.
- [DB-Kit Database Manager](https://blog.markkulab.net/en/tools/db-kit): DB-Kit is a lightweight, cross-platform database manager built with Tauri + Rust + React. Manage MySQL, MariaDB, PostgreSQL, SQL Server, Oracle, SQLite, MongoDB, Redis, Kafka, Elasticsearch and RabbitMQ from one consistent interface: passwords encrypted in the OS keychain, SSH tunnels, full CRUD, a visual query builder, stacked multi-statement result sets, cross-connection data transfer and compare/sync, Excel / CSV import & export, visualized execution plans, ER diagrams, scheduled backups, SQL stress testing with p50–p99 latency percentiles, a 15-rule SQL review engine, Kafka message browsing with monitoring & alerts, a bilingual UI (Traditional Chinese / English), a built-in AI assistant (natural-language SQL, AI review and tuning advice) and the dbk CLI. Free and open source (MIT), with installers for Windows, macOS and Linux.
- [VS Code Super Mermaid](https://blog.markkulab.net/en/tools/super-mermaid): Super Mermaid is a VS Code extension for beautiful Mermaid diagrams out of the box: auto-colored live preview, mouse pan & zoom, high-res PNG / SVG export, 21 templates and multiple themes. Free and open source (MIT).
- [React Super Mermaid](https://blog.markkulab.net/en/tools/react-super-mermaid): react-super-mermaid is an open-source React component library: render beautiful Mermaid diagrams with a single <MermaidViewer>, with built-in colorful / sketch themes, pan & zoom, in-diagram search, and high-res SVG / PNG export. Lightweight, SSR-safe, fully typed. Free and open source (MIT).
- [Jira / Confluence Super Mermaid](https://blog.markkulab.net/en/tools/jira-super-mermaid): An Atlassian Forge app: write Mermaid syntax directly inside a Jira issue or a Confluence page and get flowcharts, sequence diagrams, state machines and Gantt charts. 11 diagram types, SVG / PNG export, light and dark themes, full CJK support. Runs on Atlassian: your diagrams live in your own site and the app calls no third-party service. Free, coming soon to the Atlassian Marketplace.
- [Mermaid Live Preview](https://blog.markkulab.net/en/tools/mermaid-preview): Write Mermaid in your browser, see it render instantly, and share the whole diagram as a single link. No sign-up, nothing uploaded to a server, and mermaid.live share links work as-is.
- [React Intl Phone Number](https://blog.markkulab.net/en/tools/react-intl-phone-number): react-intl-phone-number is an open-source React component: framework-agnostic and antd-free, with E.164 in/out, a searchable flag / country-code dropdown, configurable validation levels (strict / mobile-strict / loose), themeable CSS, and i18n — phone logic powered by google-libphonenumber. Lightweight and fully typed. Free and open source (MIT).
- [Uptime Kuma Cluster](https://blog.markkulab.net/en/tools/uptime-kuma-cluster): Turn single-node Uptime Kuma into a highly available cluster: OpenResty + Lua smart load balancing, shared MariaDB state, health checks and automatic failover, plus cluster-management REST APIs. One Docker Compose command to start. Free and open source (MIT).
- [Special Education](https://blog.markkulab.net/en/education): Learning materials crafted for special education students

### Daily podcasts

- [Mark's Tech Insights — Daily AI News](https://blog.markkulab.net/en/category/tech-news): Daily curated AI and tech trends. Catch the latest developments via audio summaries — covering AI applications, software architecture, DevOps, and engineering practice. — RSS: https://blog.markkulab.net/feed.xml
- [AI股市蝦聊](https://blog.markkulab.net/en/category/ai-stock-chat): Every trading day, an AI-analyzed take on the Taiwan stock market, delivered as a two-host conversation covering the session and the next-day outlook. — RSS: https://blog.markkulab.net/ai-stock-chat/feed.xml
- [開源好物週報](https://blog.markkulab.net/en/category/open-source-weekly): A weekly two-host pick of free open-source tools surfaced from real Hacker News, GitHub, and Reddit buzz — what pain they solve and the fastest way to get started. — RSS: https://blog.markkulab.net/open-source-weekly/feed.xml

### Newsletter

[Subscribe to the newsletter](https://blog.markkulab.net/en/subscribe) — Be the first to know about new posts. No spam, unsubscribe anytime.
