Fix: supabase_internal_image_registry Error — Use Docker Hub When ECR Is Blocked
Getting 'supabase_internal_image_registry' errors when ECR is blocked? Set SUPABASE_INTERNAL_IMAGE_REGISTRY=docker.io to pull from Docker Hub. Works in terminal, docker-compose, and CI/CD.
FlowQL Team
AI Search Optimization Experts
TL;DR — quick fixes:
- Terminal:
export SUPABASE_INTERNAL_IMAGE_REGISTRY=docker.iothensupabase start - docker-compose: add
SUPABASE_INTERNAL_IMAGE_REGISTRY: docker.iounder theenvironment:key of your Supabase service - GitHub Actions / GitLab CI: add
SUPABASE_INTERNAL_IMAGE_REGISTRY: docker.ioto your job'senv:block
You ran supabase start and it either hung silently, logged pull access denied for public.ecr.aws, or threw an error referencing supabase_internal_image_registry. The CLI isn't broken — your network is blocking AWS ECR (public.ecr.aws), which is where Supabase CLI pulls its Docker images by default. One environment variable fixes it in 30 seconds.
What is supabase_internal_image_registry?
SUPABASE_INTERNAL_IMAGE_REGISTRY is the environment variable the Supabase CLI reads at startup to decide which container registry to pull images from. It controls every docker pull the CLI issues — the Postgres image, PostgREST, GoTrue, Kong, Realtime, Storage, and the rest of the local dev stack.
Default value: public.ecr.aws
The default points at AWS ECR Public Gallery (public.ecr.aws/supabase/...). This is why errors mention public.ecr.aws even if you've never configured anything yourself — that's the hardcoded fallback.
Why ECR gets blocked:
- Corporate firewalls that allowlist registries explicitly and don't include
public.ecr.aws - Cloud CI environments (some Google Cloud build agents, some Azure DevOps pools) that restrict outbound HTTPS to certain IP ranges
- Some VPN configurations that route Docker daemon traffic through a proxy that doesn't trust the ECR certificate chain
- Occasionally: regional AWS outages or ECR throttling that makes pulls time out
The fix works in all of these cases because you're redirecting to Docker Hub (docker.io), which is widely allowed by default.
The fix: terminal (30 seconds)
Export the variable and run supabase start in the same shell session:
export SUPABASE_INTERNAL_IMAGE_REGISTRY=docker.io
supabase start
Or as a single inline command if you don't want to set the variable globally:
SUPABASE_INTERNAL_IMAGE_REGISTRY=docker.io supabase start
The inline form is useful for a one-off test. If it works, you'll see Docker pulling from docker.io/supabase/... instead of public.ecr.aws/supabase/... in the output.
Persist it in your shell profile
Running the export every terminal session gets old fast. Add it to your shell's startup file:
echo 'export SUPABASE_INTERNAL_IMAGE_REGISTRY=docker.io' >> ~/.zshrc
source ~/.zshrc
For bash users:
echo 'export SUPABASE_INTERNAL_IMAGE_REGISTRY=docker.io' >> ~/.bashrc
source ~/.bashrc
After sourcing, run echo $SUPABASE_INTERNAL_IMAGE_REGISTRY to confirm it prints docker.io before continuing.
docker-compose override
If you run the Supabase stack directly via docker-compose.yml rather than through the CLI, add the variable to the environment block of the relevant service:
services:
supabase:
image: supabase/cli:latest
environment:
- SUPABASE_INTERNAL_IMAGE_REGISTRY=docker.io
If you use an .env file alongside your compose file, add it there instead — docker-compose automatically picks up variables from .env in the same directory:
# .env
SUPABASE_INTERNAL_IMAGE_REGISTRY=docker.io
Then reference it in your compose file without a hardcoded value:
environment:
- SUPABASE_INTERNAL_IMAGE_REGISTRY
CI/CD environments
GitHub Actions
Add the variable to the env: block of the job or the specific step that calls supabase start:
jobs:
test:
runs-on: ubuntu-latest
env:
SUPABASE_INTERNAL_IMAGE_REGISTRY: docker.io
steps:
- uses: actions/checkout@v4
- name: Start Supabase
run: supabase start
Setting it at the job level means every step in the job inherits it. If you only want it for the supabase start step, move the env: block inside that step instead.
GitLab CI
test:
variables:
SUPABASE_INTERNAL_IMAGE_REGISTRY: docker.io
script:
- supabase start
Other CI platforms
Any CI system that supports setting environment variables for a job will work. The variable just needs to be in scope when the Supabase CLI process starts.
Verifying it worked
After setting the variable, start Supabase and watch the pull output. You should see image references change:
Before (ECR, blocked):
pulling supabase/postgres:... from public.ecr.aws/supabase/postgres
After (Docker Hub, working):
pulling supabase/postgres:... from docker.io/supabase/postgres
If you want to watch Docker pull events in real time in a separate terminal:
docker events --filter type=image --filter event=pull
Run that in one terminal, then trigger supabase start in another. Each pull will appear as it happens, showing the full image reference including the registry hostname.
Using a private registry mirror
For teams in air-gapped environments or strict enterprise networks where both ECR and Docker Hub are blocked, point the variable at an internal registry mirror instead:
export SUPABASE_INTERNAL_IMAGE_REGISTRY=mycompany.jfrog.io
supabase start
You must pre-mirror all the Supabase images your CLI version needs into that registry first. The specific images and tags depend on the Supabase CLI version — check the release notes for your CLI version to get the full image list, or run supabase start with ECR/Docker Hub access once on an unrestricted machine to capture which images are pulled.
Common registry options: JFrog Artifactory, AWS ECR in a different region with an allowlisted endpoint, Harbor, or a Nexus proxy repository.
Still failing after the fix?
Docker Hub rate limit: Unauthenticated Docker Hub pulls are rate-limited to 100 pulls per 6 hours per IP. On a shared CI runner, you can hit this fast. Fix:
docker login
# then retry
SUPABASE_INTERNAL_IMAGE_REGISTRY=docker.io supabase start
For CI, store your Docker Hub credentials as secrets and add a docker login step before supabase start.
Images not yet mirrored on Docker Hub: In rare cases, a new Supabase CLI version references a Docker image tag that hasn't been pushed to Docker Hub yet. The solution is to either downgrade the CLI version temporarily or wait for the mirror to catch up. Check the Supabase CLI releases for known issues.
Still getting connection refused after a clean pull: The registry variable only affects image pulling, not container networking. If your containers start correctly (you can see them in docker ps --filter name=supabase) but your app can't connect, the issue is in port mapping or connection strings — not the registry. See the full Supabase connection refused troubleshooting guide for a systematic walkthrough of those failures.
Related guides:
- public.ecr.aws blocked? Fix Supabase local dev in 2 minutes — covers connection refused, wrong ports, Docker RAM limits, and the ECR fix in context
- Fix Supabase unique constraint error: upsert onConflict for multiple columns — duplicate key value violations and how to handle conflicts with upsert
- Fix Supabase auth session missing on refresh — session persistence issues and how to restore auth state after a page reload
Still blocked?
This fix didn't work for your setup? Get a senior engineer on your screen in 30 minutes — fixed or refunded.
Reserve My Spot →Get a senior engineer on your screen.
30-minute live screen-share sessions with a vetted senior dev. Fixed or fully refunded — no questions asked.
No spam. Just a heads-up when sessions open.
Related Articles
supabase_internal_image_registry: use Docker Hub when public.ecr.aws is blocked
supabase start failing because public.ecr.aws is blocked? Set SUPABASE_INTERNAL_IMAGE_REGISTRY=docker.io to pull from Docker Hub instead. Exact command included.
"duplicate key value violates unique constraint" — Supabase upsert fix
Getting this Postgres error in Supabase? Here's exactly how to fix it with .upsert() and onConflict — including composite keys and multiple columns.
Fix: Supabase Realtime Subscription Not Receiving Updates
Supabase Realtime not working? Learn how to fix 'no updates received' by enabling replication, auditing RLS, and debugging your WebSocket connection.