Docker’s login command doesn’t actually authenticate you to the registry; it just stores your credentials locally.
Let’s see it in action. First, you’ll want to create a dummy image to test with.
docker build -t my-test-image:latest .
Now, assuming you have a private registry running at my-registry.example.com, you’d log in like this:
docker login my-registry.example.com
It’ll prompt you for a username and password. Once you provide them, Docker stores these in ~/.docker/config.json. Here’s what that might look like:
{
"auths": {
"my-registry.example.com": {
"auth": "dXNlcm5hbWU6cGFzc3dvcmQ="
}
}
}
The auth field is a Base64 encoded string of username:password. This is not encrypted, so be mindful of where this file is stored.
Now, to push your image:
docker tag my-test-image:latest my-registry.example.com/my-test-image:latest
docker push my-registry.example.com/my-test-image:latest
Docker uses the credentials stored in config.json for the my-registry.example.com entry to authenticate the push request.
The core problem this solves is securely getting your container images from your local development environment into a central, accessible location for deployment. Without authentication, anyone could pull or push images to your registry, leading to security breaches and corrupted deployments.
Internally, when you docker push, the Docker daemon makes an HTTP request to the registry’s API. It includes an Authorization header in the format Basic <base64_encoded_credentials>. The registry then decodes these credentials and verifies them against its user database. If they match, the push is allowed.
The docker login command is essentially a convenience wrapper. It prompts for credentials, encodes them, and writes them to the config.json file. You can also achieve the same result by directly editing config.json or by using environment variables for CI/CD systems.
Here’s a common pattern for non-interactive logins, especially in automated scripts or CI/CD pipelines:
echo "$REGISTRY_PASSWORD" | docker login my-registry.example.com --username "$REGISTRY_USERNAME" --password-stdin
This pipes your password directly to the docker login command, which reads it from standard input. The --password-stdin flag is crucial here; without it, docker login would still try to prompt interactively. The credentials are then written to ~/.docker/config.json as before.
Many people assume that docker login performs a handshake with the registry to confirm the credentials are valid. In reality, it’s a local operation. The actual authentication happens during the push or pull operation. This means you could docker login with incorrect credentials, and only discover the error when you attempt to push or pull an image.
A subtle but important detail is how Docker handles multiple registries. If you have entries for docker.io (the default Docker Hub) and your private registry, each will have its own auth entry in config.json. Docker intelligently selects the correct credentials based on the registry hostname specified in your docker tag and docker push commands.
If your registry uses TLS certificates that aren’t trusted by your system’s default trust store, you might encounter TLS handshake errors during login or push. In such cases, you’ll need to configure Docker to trust your registry’s certificate, often by placing the CA certificate in /etc/docker/certs.d/<your-registry-hostname>:<port>/ca.crt.
The next hurdle you’ll likely face is managing image tags effectively across multiple environments.