目录
推送镜像到 Docker Hub 与私有 Registry 教程:发布和下载镜像
在自己的电脑上构建好镜像后,如果要在服务器上运行或分享给团队,就需要将它推送到 Registry。最常用的是 Docker Hub,其次是 GitHub Container Registry(ghcr.io)以及各云平台的 Registry。
镜像名称决定推送到哪里
推送时,Docker 根据镜像名称决定要发送到哪个 Registry、哪个账号(命名规则见镜像):
| 名称 | 推送到 |
|---|---|
yourname/mysite:1.0.0 | Docker Hub 的 yourname 账号 |
ghcr.io/yourname/mysite:1.0.0 | GitHub Container Registry |
localhost:5000/mysite:1.0.0 | 本地 5000 端口上的私有 Registry |
因此,推送前要先用 docker tag 将镜像命名为对应的名称。
未登录就推送会怎样
先看看未登录时直接推送到 Docker Hub 的结果:
$ docker tag nginx:1.30 yourname/mysite:1.0.0
$ docker push yourname/mysite:1.0.0
The push refers to repository [docker.io/yourname/mysite]
f4616bb1be1c: Unavailable
f1a6a629b824: Unavailable
3d774efa34e3: Unavailable
ecc510c1e359: Unavailable
push access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
push access denied ... authorization failed:Docker Hub 拒绝了推送,因为你尚未登录,而且 yourname 也不是你的账号。下面依次完成注册、登录和推送。
推送到 Docker Hub
1. 注册账号
前往 Docker 官网注册账号(可以使用 Email,也可以直接使用 Google 或 GitHub 账号)。Username 就是之后镜像名称前面的命名空间:

2. 创建访问令牌并登录
建议在 Docker Hub 的账号设置中创建 Personal Access Token,用它代替密码登录,并且可以限制权限(例如只允许读写,不允许删除)。
docker login -u yourname
按提示粘贴访问令牌。登录成功后会显示 Login Succeeded。
3. 创建储存库(可选)
首次推送时,Docker Hub 会自动创建储存库。如果想提前设置说明或将其设为私有,可以在 Docker Hub 网站上点击 Create repository:

Docker Hub 上的储存库默认是公开的。免费方案的私有储存库数量有限,详见 Docker Hub 的方案说明。
4. 标记并推送
docker build -t yourname/mysite:1.0.0 -t yourname/mysite:latest .
docker push yourname/mysite:1.0.0
docker push yourname/mysite:latest
-t 可以多次使用,一次为镜像命名多个标签。也可以使用 docker push --all-tags yourname/mysite 一次推送所有标签。推送输出的格式与下面“自建私有 Registry”中的实际示例相同。
推送后,在 Docker Hub 储存库的 Tags 选项卡中可以看到每个标签,以及供其他人下载的命令:

5. 在其他机器上使用
docker pull yourname/mysite:1.0.0
docker run -d -p 8080:80 yourname/mysite:1.0.0
自建私有 Registry:实际演示推送与下载
Docker 官方提供了 registry 镜像,可以快速搭建一个私有 Registry。这也是亲自体验“推送”最简单的方法,不需要任何账号:
$ docker run -d -p 5000:5000 --restart unless-stopped -v registry-data:/var/lib/registry --name registry registry:3
Unable to find image 'registry:3' locally
3: Pulling from library/registry
e2de96513ba9: Already exists
90764beeca7f: Pull complete
8dc2188d2a74: Pull complete
783ed5b12fca: Pull complete
2aef3a8beb68: Pull complete
Digest: sha256:ddf754342cfc8acc51a56d5d0ab6af06826461864460636d8bd5c546dab2a7b8
Status: Downloaded newer image for registry:3
c5d02febbf0fd6d8c17e6818990adb28fd0a97c380a0d4c0a58ec793f685e4a0
标记并推送:
$ docker tag nginx:1.30 localhost:5000/mysite:1.0.0
$ docker tag nginx:1.30 localhost:5000/mysite:latest
$ docker push localhost:5000/mysite:1.0.0
The push refers to repository [localhost:5000/mysite]
3d774efa34e3: Pushed
ecc510c1e359: Pushed
f4616bb1be1c: Pushed
f1a6a629b824: Pushed
cb44c3fe542b: Pushed
121907e00c1c: Pushed
220a6ac36368: Pushed
1.0.0: digest: sha256:6e2a172ae5503c0df64cd3294b57c86394ced402016a8cc45436086fa51f5d02 size: 2288
i Info → Not all multiplatform-content is present and only the available single-platform image was pushed
sha256:9bf97bd7714f5e24c1ccd545ecb9eb5435cb6d109c97cebb15e7e455e0239edb -> sha256:6e2a172ae5503c0df64cd3294b57c86394ced402016a8cc45436086fa51f5d02
$ docker push --all-tags localhost:5000/mysite
The push refers to repository [localhost:5000/mysite]
220a6ac36368: Layer already exists
3d774efa34e3: Layer already exists
ecc510c1e359: Layer already exists
f4616bb1be1c: Layer already exists
f1a6a629b824: Layer already exists
cb44c3fe542b: Layer already exists
121907e00c1c: Layer already exists
1.0.0: digest: sha256:6e2a172ae5503c0df64cd3294b57c86394ced402016a8cc45436086fa51f5d02 size: 2288
220a6ac36368: Layer already exists
3d774efa34e3: Layer already exists
ecc510c1e359: Layer already exists
f4616bb1be1c: Layer already exists
f1a6a629b824: Layer already exists
cb44c3fe542b: Layer already exists
121907e00c1c: Layer already exists
latest: digest: sha256:6e2a172ae5503c0df64cd3294b57c86394ced402016a8cc45436086fa51f5d02 size: 2288
i Info → Not all multiplatform-content is present and only the available single-platform image was pushed
sha256:9bf97bd7714f5e24c1ccd545ecb9eb5435cb6d109c97cebb15e7e455e0239edb -> sha256:6e2a172ae5503c0df64cd3294b57c86394ced402016a8cc45436086fa51f5d02
i Info → Not all multiplatform-content is present and only the available single-platform image was pushed
sha256:9bf97bd7714f5e24c1ccd545ecb9eb5435cb6d109c97cebb15e7e455e0239edb -> sha256:6e2a172ae5503c0df64cd3294b57c86394ced402016a8cc45436086fa51f5d02
- 首次推送时,每一层都会显示 Pushed(已推送)。
- 使用
--all-tags推送latest时,所有层都显示 Layer already exists:1.0.0和latest是同一个镜像,Registry 中已经有这些层,无需重新上传。 - 最后的
Info是 Docker 29 的提示:本地只下载了linux/amd64这个平台的 nginx,因此推送的也只有这个平台。要推送多平台镜像,请参见多平台镜像。
使用 Registry 的 API 确认内容:
$ curl -s http://localhost:5000/v2/_catalog
{"repositories":["mysite"]}
$ curl -s http://localhost:5000/v2/mysite/tags/list
{"name":"mysite","tags":["1.0.0","latest"]}
删除本地版本,再从自己的 Registry 下载回来:
$ docker rmi localhost:5000/mysite:1.0.0 localhost:5000/mysite:latest
Untagged: localhost:5000/mysite:1.0.0
Untagged: localhost:5000/mysite:latest
$ docker pull localhost:5000/mysite:1.0.0
1.0.0: Pulling from mysite
Digest: sha256:6e2a172ae5503c0df64cd3294b57c86394ced402016a8cc45436086fa51f5d02
Status: Downloaded newer image for localhost:5000/mysite:1.0.0
localhost:5000/mysite:1.0.0
这样搭建的 Registry 没有账号验证,也没有 HTTPS,只适合在本地或内部网络中测试。可以直接使用
localhostRegistry;Docker 会拒绝其他地址的 HTTP Registry,需要另外配置insecure-registries。正式使用时请添加 TLS 和验证,或使用云平台提供的 Registry(例如 AWS ECR、Google Artifact Registry、Azure Container Registry)。
推送到 GitHub Container Registry(ghcr.io)
适合代码存放在 GitHub 上的项目。先在 GitHub 创建一个具有 write:packages 权限的 Personal Access Token,然后执行:
echo $GITHUB_TOKEN | docker login ghcr.io -u 你的GitHub账号 --password-stdin
docker tag mysite ghcr.io/你的账号/mysite:1.0.0
docker push ghcr.io/你的账号/mysite:1.0.0
--password-stdin 从标准输入读取密码,避免密码出现在命令历史记录中。
标签命名建议
| 做法 | 示例 | 说明 |
|---|---|---|
| 语义化版本 | 1.4.2、1.4、1 | 同时推送多个层级,让用户自行选择更新幅度 |
| Git commit | sha-3f2a1c9 | 每个镜像都能对应到确切的代码 |
| 环境 | staging、production | 可变标签,指向当前部署的版本 |
latest | latest | 方便试用,但生产环境不要依赖它 |
正式部署时请使用固定的版本号,这样才能知道服务器上运行的是哪个版本,也能快速回退到上一版(参见部署到云主机)。
登出与凭据
docker logout
docker logout ghcr.io
登录信息默认保存在 ~/.docker/config.json。Docker Desktop 会改用操作系统的凭据管理器(Windows 凭据管理器、macOS 钥匙串)保存,更安全。
重点回顾
- 镜像名称的前缀决定推送到哪个 Registry。
- 使用访问令牌(Access Token)登录,不要使用账号密码。
- 使用
docker tag添加目标名称,再执行docker push。 - 生产环境使用固定版本号标签,不要依赖
latest。
动手练习
练习 1:按照本章的方法启动本地 Registry(registry:3,对应 5000 端口),将 alpine:3.23 推送上去,删除本地副本后再下载回来。
答案
docker run -d -p 5000:5000 --name registry registry:3
docker tag alpine:3.23 localhost:5000/alpine:3.23
docker push localhost:5000/alpine:3.23
docker rmi localhost:5000/alpine:3.23
docker pull localhost:5000/alpine:3.23
常见问题
推送时出现 denied: requested access to the resource is denied?
通常是因为尚未登录,或者镜像名称中的账号部分不是你的账号。名称应写成 你的账号/储存库:tag,并先执行 docker login。
Docker Hub 的私有储存库要付费吗?
免费账号可以创建少量私有储存库,数量和限制以 Docker Hub 官方方案说明为准。也可以改用 GitHub Container Registry。
可以自动推送吗?
可以使用 GitHub Actions,在每次推送代码或创建版本标签时自动构建并推送,参见 [GitHub Actions 自动构建](/zh/docker/ci-cd/)。
更新于 2026年10月14日