Docker Mirror
Automatic selection of the fastest Iranian Docker registry mirror for
docker pullanddocker compose upcommands.
In This Document
The Problem
Direct access to Docker Hub from Iran is often slow or unreachable. This tool transparently and automatically:
- Finds the fastest reachable Iranian mirror
- Updates Docker daemon configuration
- Executes your original command
No changes to your existing Docker workflow are required. The tool wraps your existing commands behind the scenes.
Prerequisites
| Tool | Status | Installation |
|---|---|---|
curl | Required | Pre-installed on most systems |
jq | Recommended | brew install jq or sudo apt install jq |
python3 | Fallback for jq | Pre-installed on most systems |
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:
- Recommended — Source (Activate Immediately)
- Alternative — Bash (Requires New Terminal)
source docker/setup_docker_mirrors.sh
bash docker/setup_docker_mirrors.sh
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
| Step | Action |
|---|---|
| 1 | Shell detection — Identifies your shell type (zsh / bash / sh) |
| 2 | File copy — Copies wrapper functions to ~/.docker_mirror_functions.sh |
| 3 | Injection — Adds a single source line to your shell RC file (e.g., ~/.zshrc, ~/.bashrc) |
| 4 | Auto-activation — When run via source, activates the functions in your current shell immediately |
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:
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:
docker ps
docker logs my-container
docker exec -it app bash
docker images
docker network ls
Mirror List
Mirrors are probed in priority order:
| Priority | Provider | Address |
|---|---|---|
| 1 | Liara | docker-mirror.liara.ir |
| 2 | ArvanCloud | docker.arvancloud.ir |
| 3 | MobinHost | docker.mobinhost.com |
Selection Logic
- Each mirror is tested via HTTP request to its
/v2/endpoint - HTTP
200or401response = 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:
| Property | Value |
|---|---|
| Cache file | ~/.docker_mirror_cache |
| Default TTL | 300 seconds (5 minutes) |
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):
- zsh (~/.zshrc)
- bash (~/.bashrc)
# 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
# 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:
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
bash docker/setup_docker_mirrors.sh --remove
This command will:
- Remove the
sourceline from your shell RC files - Delete
~/.docker_mirror_functions.sh - Delete the cache file
~/.docker_mirror_cache
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
| Shell | Config File | Status |
|---|---|---|
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_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
│ Liara → Arvan → Mobin│ 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 Case | Handling |
|---|---|
daemon.json doesn't exist | Created automatically |
daemon.json has invalid JSON | Backed up to .bak.<timestamp>, reset to {} |
| No mirrors reachable | Warning printed, command runs via Docker Hub |
| Mirror already active | Skips write & daemon reload |
Missing curl or jq | Warning printed, command runs without mirror |
docker ps, docker logs, etc. | Pass-through with zero overhead |
Troubleshooting
| Problem | Solution |
|---|---|
| Repeated password prompts | Run sudo -v before a batch of commands |
| No mirror selected | Enable DOCKER_MIRROR_VERBOSE=1 and check output |
jq/python error | Install jq: brew install jq |
Corrupt daemon.json | Check /etc/docker/ for .bak.* backup files |
| Not working after install | Run 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