Fix "Error Saving Credentials" During Docker Login
When I tried to do docker login -u username
, I got below error.
Error saving credentials: error storing credentials - err: exit status 1, out:
`Post "https://hub.docker.com/v2/users/login?refresh_token=true": EOF`
My environment:
- macOS 13.4 (22F66)
- Docker Desktop 4.20.1 (110738)
I got the same error popped up even when I tried to sign in in the Docker Desktop app.
The Fix
After googled for a while, I found this Stack Overflow answer helpful.
As per the answer, removing the ~/.docker/config.json
file fixes the problem.
(For safety, you can rename it instead of removing it.)
Docker will create a new config.json
file once you delete it.
The content of the old one I removed.
$ cat ~/.docker/config.json-bak
{
"auths": {},
"credsStore": "desktop",
"currentContext": "desktop-linux"
}
The one created by docker, which doesn’t block docker login
.
$ cat ~/.docker/config.json
{
"auths": {
"https://index.docker.io/v1/": {}
},
"credsStore": "osxkeychain"
}
Change the credsStore
property from the value "desktop"
to "osxkeychain"
is the key of the fix.
This property is to specify an external binary to serve as the default credential store.
Setting to "osxkeychain"
means using the docker-credential-osxkeychain
helper.
There are different credentials helpers. For example, after installed the Docker Desktop in my Mac, I have below helpers installed.
$ docker-credential-[TAB]
docker-credential-desktop docker-credential-ecr-login docker-credential-osxkeychain
It’s said the docker-credential-desktop
is just a wrapper of the docker-credential-osxkeychain
on macOS. If it’s so, "credsStore": "desktop"
was supposed to work.
Maybe there is some bug in the -desktop
helper.
Note that, removing the credsStore
property in the config.json
file also fixes the problem.
Docker will fallback to the default behavior if the property is absent:
By default, Docker looks for the native binary on each of the platforms, i.e. “osxkeychain” on macOS, “wincred” on windows
Deploy a Rails App with Docker
This site is deployed using Docker containers on a small DigitalOcean Droplet.
Since I never have a private Docker registry, the original deploy flow was,
- push the source code to the remote Git repository
- on the Droplet, fetch the source code from the Git remote
- build an Docker image from the source code
- start the Docker container
Since some time ago, the docker build
has kept failing on the bundle install
instruction on the Droplet.
During bundle install
, the Bundler fails to build the sassc
gem.
The Bundler complains that,
virtual memory exhausted: Cannot allocate memory
, while building the sassc
gem.
A workaround I tried was,
- build the Docker image on a more powerful machine, like the local dev machine
- save the image into a tar archive (
docker save img-foo | bzip2 > foo.img.bz
) - copy the tar to the Droplet
- load the image from the tar (
bunzip2 foo.img.bz && docker load -i foo.img
) - start the Docker container
One problem of the workaround was the volume to transfer is a bit large (the image is around 1G before compression, and 370M after compression). It was time consuming over a slow network.
Current Deploy Workflow
Actually, Bundler supports fetching gems from local cache, not only from rubygems.org
.
Using bundle package --all
can,
Copy all of the .gem files needed to run the application into the vendor/cache directory.
In the future, when running bundle install, use the gems in the cache in preference to the ones on rubygems.org.
The sassc
gem is built on the local machine while bundle package
, and put into vendor/cache
directory.
Now, the deploy flow is like,
- run
bundle package --all
if the application’s dependencies change - check in
vendor/cache
folder and push it to the Git remote - on the Droplet, fetch the source code from the Git remote
- build an Docker image from the source code
- start the Docker container
The Dockerfile
is like,
USER app
RUN mkdir -p /home/app/source-code
WORKDIR /home/app/source-code
COPY vendor/cache ./vendor/cache/
COPY Gemfile* ./
# use RVM, so "bash -lc ..."
RUN bash -lc 'bundle install --deployment --without development test'
# copy the app source code
COPY . ./
Other Notes
If a gem is located at a particular git repository using the :git
parameter, like
gem "rails", "2.3.8", :git => "https://github.com/rails/rails.git"
Then the --all
option should be used in bundle package
.
Some gems are built using CMake.
When run bundle package --all
, CMakeCache.txt
files are created and stored in vendor/cache
.
The problem with CMakeCache.txt
files is that they use hardcoded paths of the machine runs bundle package --all
.
When build the Docker image in the remote server, the hardcoded paths fail the bundle install
.
Therefore, in the .dockerignore
file, add the following line.
vendor/cache/*/CMakeCache.txt