Skip to main content

Docker Mirror

Automatic selection of the fastest Iranian Docker registry mirror for docker pull and docker compose up commands.

In This Document


The Problem

Direct access to Docker Hub from Iran is often slow or unreachable. This tool transparently and automatically:

  1. Finds the fastest reachable Iranian mirror
  2. Updates Docker daemon configuration
  3. Executes your original command
No Workflow Changes Required

No changes to your existing Docker workflow are required. The tool wraps your existing commands behind the scenes.


Prerequisites

ToolStatusInstallation
curlRequiredPre-installed on most systems
jqRecommendedbrew install jq or sudo apt install jq
python3Fallback for jqPre-installed on most systems
warning

If neither jq nor python3 is available, the wrapper prints a warning and the command runs without mirror configuration.


Installation

Run from the project root:

Install + activate immediately (no restart needed)
source docker/setup_docker_mirrors.sh
Why source?

Running with bash creates a child process that cannot modify your current shell session. Using source lets the script activate the wrapper functions in your current shell immediately after installation — no terminal restart needed.

What the Setup Script Does

StepAction
1Shell detection — Identifies your shell type (zsh / bash / sh)
2File copy — Copies wrapper functions to ~/.docker_mirror_functions.sh
3Injection — Adds a single source line to your shell RC file (e.g., ~/.zshrc, ~/.bashrc)
4Auto-activation — When run via source, activates the functions in your current shell immediately
Idempotent Script

The setup script is idempotent — safe to run multiple times without creating duplicate entries.


Usage

Normal Usage — Zero Change Required

After installation, use Docker exactly as before. No command changes needed:

Commands that automatically use the best mirror
docker pull nginx:latest
docker pull redis:7-alpine
docker compose up -d
docker compose pull
docker-compose up # v1 syntax also supported

Unaffected Commands

All other Docker commands pass through with zero overhead:

Pass-through commands (no mirror overhead)
docker ps
docker logs my-container
docker exec -it app bash
docker images
docker network ls

Mirror List

Mirrors are probed in priority order:

PriorityProviderAddress
1Liaradocker-mirror.liara.ir
2ArvanClouddocker.arvancloud.ir
3MobinHostdocker.mobinhost.com

Selection Logic

  • Each mirror is tested via HTTP request to its /v2/ endpoint
  • HTTP 200 or 401 response = mirror is alive
  • The mirror with the lowest response time wins
  • If no mirror is reachable → command runs directly via Docker Hub

Caching

To avoid redundant mirror checks, the result is cached for 5 minutes:

PropertyValue
Cache file~/.docker_mirror_cache
Default TTL300 seconds (5 minutes)
tip

Consecutive docker pull commands within the TTL reuse the cached mirror without re-probing.


Advanced Configuration

All settings are configurable via environment variables. Add them to your shell RC file (e.g., ~/.zshrc):

~/.zshrc — Advanced configuration
# Cache TTL in seconds (default: 300 = 5 minutes)
export DOCKER_MIRROR_CACHE_TTL=600

# Per-mirror probe timeout in seconds (default: 4)
export DOCKER_MIRROR_CURL_TIMEOUT=3

# Enable debug logging (default: 0 = off)
export DOCKER_MIRROR_VERBOSE=1

# Docker daemon.json path (default: /etc/docker/daemon.json)
export DOCKER_MIRROR_DAEMON_JSON=/etc/docker/daemon.json

# Cache file path (default: ~/.docker_mirror_cache)
export DOCKER_MIRROR_CACHE_FILE=$HOME/.docker_mirror_cache

Debug Mode

To see the mirror selection process in real time:

One-time verbose execution
DOCKER_MIRROR_VERBOSE=1 docker pull alpine
# Example output:
# [docker-mirror] Checking Iranian registry mirrors...
# [docker-mirror] Probing https://docker-mirror.liara.ir ...
# [docker-mirror] -> https://docker-mirror.liara.ir responded in 245ms
# [docker-mirror] Probing https://docker.arvancloud.ir ...
# [docker-mirror] -> https://docker.arvancloud.ir responded in 380ms
# [docker-mirror] Probing https://docker.mobinhost.com ...
# [docker-mirror] -> https://docker.mobinhost.com unreachable
# [docker-mirror] Selected mirror: https://docker-mirror.liara.ir
# [docker-mirror] Applied mirror: https://docker-mirror.liara.ir

Uninstall

Remove Docker Mirror wrapper
bash docker/setup_docker_mirrors.sh --remove

This command will:

  • Remove the source line from your shell RC files
  • Delete ~/.docker_mirror_functions.sh
  • Delete the cache file ~/.docker_mirror_cache
daemon.json Not Reverted

Docker's daemon.json is not reverted automatically. Edit /etc/docker/daemon.json manually if needed after uninstalling.


Important Notes

sudo Access

Modifying /etc/docker/daemon.json and reloading the Docker service requires sudo privileges. You may be prompted for your system password on the first docker pull after a cache miss.

Shell Compatibility

ShellConfig FileStatus
zsh~/.zshrc✅ Supported
bash~/.bashrc / ~/.bash_profile✅ Supported
sh / dash / ksh~/.profile✅ Supported

Customizing the Mirror List

Edit ~/.docker_mirror_functions.sh to add or reorder mirrors:

~/.docker_mirror_functions.sh — Custom mirror list
DOCKER_MIRROR_LIST=(
"https://docker-mirror.liara.ir"
"https://docker.arvancloud.ir"
"https://docker.mobinhost.com"
"https://your-custom-mirror.example.com" # Add your own
)

How It Works

┌─────────────────────────┐
│ docker pull nginx │ You type the command
└───────────┬─────────────┘

┌──────▼───────┐
docker()Shell wrapper function intercepts
│ wrapper │
└──────┬───────┘

┌──────▼────────────────┐
Read cache │ Check ~/.docker_mirror_cache (TTL: 5 min)
└──────┬────────────────┘
│ cache miss
┌──────▼────────────────┐
Probe mirrors │ curl /v2/ on each mirror
LiaraArvanMobin│ Pick fastest (HTTP 200/401)
└──────┬────────────────┘

┌──────▼────────────────┐
Apply mirror │ jq merge into daemon.json
│ │ + reload Docker daemon
└──────┬────────────────┘

┌──────▼────────────────┐
│ command docker pull │ Execute original command
│ nginx │
└───────────────────────┘

Safety Features

Edge CaseHandling
daemon.json doesn't existCreated automatically
daemon.json has invalid JSONBacked up to .bak.<timestamp>, reset to {}
No mirrors reachableWarning printed, command runs via Docker Hub
Mirror already activeSkips write & daemon reload
Missing curl or jqWarning printed, command runs without mirror
docker ps, docker logs, etc.Pass-through with zero overhead

Troubleshooting

ProblemSolution
Repeated password promptsRun sudo -v before a batch of commands
No mirror selectedEnable DOCKER_MIRROR_VERBOSE=1 and check output
jq/python errorInstall jq: brew install jq
Corrupt daemon.jsonCheck /etc/docker/ for .bak.* backup files
Not working after installRun source docker/setup_docker_mirrors.sh again, or open a new terminal

File Structure

docker/
├── docker_mirror_functions.sh # Core wrapper functions (copied to ~/.docker_mirror_functions.sh)
├── setup_docker_mirrors.sh # Install / uninstall script
├── DOCKER_MIRROR_FA.md # Persian documentation
└── DOCKER_MIRROR_EN.md # English documentation