Bug report
Describe the bug
When using a root-level workspace deno.json at supabase/functions/deno.json (instead of per-function deno.json files), supabase functions deploy fails to resolve the environment variable in .npmrc.
The .npmrc file uses ${NPM_AUTH_TOKEN} for private registry authentication. functions serve reads this variable correctly (from supabase/functions/.env), but functions deploy does not — resulting in a registry authentication failure.
.npmrc configuration
@myorg:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NPM_AUTH_TOKEN}
The NPM_AUTH_TOKEN is provided via supabase/functions/.env, which functions serve loads automatically.
Project structure
supabase/functions/
├── deno.json # Root workspace config
│ ├── "workspace": ["./*"]
│ └── "imports": {
│ "@my-private-pkg": "npm:@myorg/my-package@^1.0.0",
│ ...
│ }
├── .npmrc # Private registry auth
├── .env # NPM_AUTH_TOKEN=ghp_xxxxx
├── function-a/
│ ├── index.ts
│ └── deno.json # Function-specific imports only
├── function-b/
│ └── ...
└── _shared/ # Shared utilities
Why this structure?
The official docs recommend per-function deno.json, but this creates a dependency management problem with _shared/ code — shared utilities would need their npm dependencies duplicated across every function's deno.json. A root workspace deno.json solves this. This approach is also discussed as practical in Discussion #33595.
Steps to reproduce
- Set up the project structure above with a private npm package
- Configure
.npmrc with ${NPM_AUTH_TOKEN} environment variable
- Set the token in
supabase/functions/.env
- Run
supabase functions serve → Works, private package resolves correctly
- Run
supabase functions deploy → Fails, authentication error (.npmrc env var not resolved)
Expected behavior
functions deploy should resolve environment variables in .npmrc the same way functions serve does.
Workaround
Running functions serve before deploy makes it work. The serve process appears to cache the resolved npm packages, which deploy then picks up:
# In CI/CD pipeline:
- name: Start Supabase services
run: supabase start -x imgproxy,supavisor,realtime,storage-api,studio,mailpit,logflare,vector
- name: Warm up functions (workaround)
run: |
supabase functions serve --no-verify-jwt &
SERVE_PID=$!
sleep 3
# Must call a function that imports the private package
curl -i --fail http://127.0.0.1:54321/functions/v1/health || true
kill $SERVE_PID || true
wait $SERVE_PID 2>/dev/null || true
- name: Deploy Edge Functions
run: supabase functions deploy --no-verify-jwt --prune --yes
Important: The health endpoint must actually import the private package — simply running serve is not enough. The package needs to be loaded at least once.
// health/index.ts
import {} from "@my-private-pkg"; // Force package resolution
Deno.serve(() => new Response("ok", { status: 200 }));
This was discovered empirically after multiple failed attempts and has been the only reliable CI/CD method.
Environment
- Supabase CLI: v2.75.0
- Deno: v2.7.1
- OS: Ubuntu 24.04 (GitHub Actions) / macOS (local)
Related issues
Bug report
Describe the bug
When using a root-level workspace
deno.jsonatsupabase/functions/deno.json(instead of per-functiondeno.jsonfiles),supabase functions deployfails to resolve the environment variable in.npmrc.The
.npmrcfile uses${NPM_AUTH_TOKEN}for private registry authentication.functions servereads this variable correctly (fromsupabase/functions/.env), butfunctions deploydoes not — resulting in a registry authentication failure..npmrcconfigurationThe
NPM_AUTH_TOKENis provided viasupabase/functions/.env, whichfunctions serveloads automatically.Project structure
Why this structure?
The official docs recommend per-function
deno.json, but this creates a dependency management problem with_shared/code — shared utilities would need their npm dependencies duplicated across every function'sdeno.json. A root workspacedeno.jsonsolves this. This approach is also discussed as practical in Discussion #33595.Steps to reproduce
.npmrcwith${NPM_AUTH_TOKEN}environment variablesupabase/functions/.envsupabase functions serve→ Works, private package resolves correctlysupabase functions deploy→ Fails, authentication error (.npmrcenv var not resolved)Expected behavior
functions deployshould resolve environment variables in.npmrcthe same wayfunctions servedoes.Workaround
Running
functions servebeforedeploymakes it work. The serve process appears to cache the resolved npm packages, which deploy then picks up:Important: The health endpoint must actually
importthe private package — simply running serve is not enough. The package needs to be loaded at least once.This was discovered empirically after multiple failed attempts and has been the only reliable CI/CD method.
Environment
Related issues