Answers · Deploying

Why does Kamal fail with pull access denied on deploy?

The server cannot log in to the registry that holds your image. Kamal pushes from your machine, where you are already authenticated, then asks the server to pull, where it is not. Check the registry block in config/deploy.yml names the right username, and that KAMAL_REGISTRY_PASSWORD is actually set in .kamal/secrets.

Confirm what the server is being told

bin/kamal secrets print
bin/kamal registry login

secrets print shows the names and whether they resolved, which is the fast way to spot a blank value. registry login runs the authentication step on its own, so you get the registry's own error message rather than a failed pull three steps later.

The usual causes, in order

The secret is not exported. .kamal/secrets is not committed, by design, so a fresh clone or a new CI runner has no registry password at all. The file reads values from your environment or a password manager; if the variable is unset, the secret is empty and the error is an authentication failure rather than a missing-value error.

The token expired or was scoped wrong. A GitHub Container Registry token needs write:packages to push and read:packages to pull. A token with only the write scope pushes successfully from your laptop and then fails on the server, which is exactly the shape of this error.

The image name does not match the registry. For GHCR the image is ghcr.io/<owner>/<repo>, and the owner is case sensitive in some clients. A mismatch here authenticates fine and then fails to find the image, with a similar looking message.

Private registries and a fresh server

A server that has never pulled from this registry has no stored credentials. Kamal writes them during kamal setup, so a server provisioned by hand, or rebuilt, needs setup run again rather than just a deploy.

bin/kamal setup

That is also the moment to confirm the rest of the secrets resolve, because bin/kamal setup sends them all. RAILS_MASTER_KEY is the one that turns a registry problem into a boot problem the moment the pull starts working.

Rule out the network before you chase credentials

ssh your-server 'docker pull hello-world'

If that fails too, the problem is outbound network or DNS on the host, not your registry configuration, and no amount of token fixing will help.

Related questions

What do I need before a first deploy to production?

Four things: a Linux server you can SSH into, somewhere to push a container image, a domain pointed at the server, and RAILS_MASTER_KEY. That last one is the only secret production will not boot without. Billing, email and bot checking all fall back to in-process fakes, so you can ship before you have those keys.

Why does a Kamal deploy hang on the health check?

The new container started and exited before Rails could answer /up. Kamal waits, then rolls back and leaves the old container serving. Run kamal app logs to see the real error. On a first deploy it is almost always a missing or wrong RAILS_MASTER_KEY, which raises before any route exists to answer the check.

Can I run SQLite in production for a Rails app?

Yes, for one server. Rails 8 ships SQLite configured for production use, and One Shot runs on it: the database is a file on a mounted volume, backups are a file copy, and there is no second service to operate. The limit is real though. Two application servers cannot write to the same SQLite file, so horizontal scaling is where it stops.