Edge Functions are one of Supabase's most powerful features—serverless TypeScript functions running on Deno that execute close to your users. But if you've tried to set them up on a self-hosted Supabase instance, you've probably discovered it's not as straightforward as the cloud version.
This guide covers everything you need to know about running Edge Functions on your own infrastructure: from initial setup to production-ready deployment.
The Current State of Self-Hosted Edge Functions
Let's be honest about where things stand. Self-hosted Edge Functions are officially in beta, and Supabase warns that breaking changes to APIs and configuration options will happen. The community has felt this pain—recent JWT verification changes in early 2026 broke many self-hosted deployments without warning.
Here's what you should know:
- Studio integration is limited: The Edge Functions icon doesn't appear in self-hosted Studio by default
- Logs can be problematic: Many users report 502 errors when accessing function logs
- Cron scheduling requires extra setup: Unlike cloud Supabase, scheduled functions need manual configuration
- Documentation gaps exist: Some features work differently than documented
Despite these challenges, self-hosted Edge Functions absolutely work. You just need to understand the architecture and configuration requirements.
Understanding the Architecture
Supabase Edge Functions run on the Edge Runtime, an open-source Deno-based server that Supabase maintains. When you deploy functions on Supabase Cloud, they run on Deno Deploy with a functions-relay handling authentication and rate limiting.
For self-hosted deployments, you have two options:
- Use the bundled edge-functions container in the official Docker Compose stack
- Deploy Edge Runtime separately on platforms like Fly.io, Railway, or your own servers
The bundled approach is simpler but less flexible. Deploying separately gives you more control over scaling and geographic distribution.
Setting Up Edge Functions with Docker Compose
If you're running self-hosted Supabase via Docker Compose, Edge Functions are already included—they just need configuration.
Step 1: Verify the Edge Functions Service
Check your docker-compose.yml for the functions service:
functions:
container_name: supabase-edge-functions
image: supabase/edge-runtime:v1.69.28
restart: unless-stopped
depends_on:
analytics:
condition: service_healthy
environment:
JWT_SECRET: ${JWT_SECRET}
SUPABASE_URL: http://kong:8000
SUPABASE_ANON_KEY: ${ANON_KEY}
SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY}
SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/postgres
VERIFY_JWT: "true"
Step 2: Create Your Functions Directory
Edge Functions are stored in volumes/functions. Create the directory structure:
mkdir -p volumes/functions/hello
Create your first function at volumes/functions/hello/index.ts:
import { serve } from "https://deno.land/[email protected]/http/server.ts"
serve(async (req) => {
const { name } = await req.json()
const data = {
message: `Hello ${name}!`,
}
return new Response(
JSON.stringify(data),
{ headers: { "Content-Type": "application/json" } },
)
})
Step 3: Configure Environment Variables
Your .env file needs these Edge Function-specific variables:
# Edge Functions FUNCTIONS_VERIFY_JWT=true # Make sure these match your existing config JWT_SECRET=your-super-secret-jwt-key-at-least-32-characters ANON_KEY=your-anon-key SERVICE_ROLE_KEY=your-service-role-key
Step 4: Restart and Test
docker compose down docker compose up -d
Test your function:
curl -X POST 'http://localhost:8000/functions/v1/hello' \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "World"}'
Troubleshooting Common Issues
JWT Verification Errors
The most common issue in 2026 is JWT verification failures. Supabase recently transitioned to asymmetric JWT keys, causing errors like:
TypeError: Key for the ES256 algorithm must be of type CryptoKey
Quick fix: Pin to Edge Runtime version 1.69.28 in your docker-compose.yml:
functions: image: supabase/edge-runtime:v1.69.28
Long-term solution: Update your JWT configuration to use the new asymmetric format. Check the official migration guide for specifics.
Functions Not Appearing in Studio
Self-hosted Studio doesn't show the Functions sidebar by default. This is a known limitation. You have two options:
- Manage functions via CLI: Use
supabase functionscommands - Deploy through your CI/CD pipeline: Most production setups use automated deployment anyway
502 Errors on Function Logs
If you see "Something went wrong! Unknown error" when accessing logs:
- Check that the analytics service is running:
docker logs supabase-analytics - Verify the functions container can reach analytics:
docker exec supabase-edge-functions ping analytics - Check disk space—log ingestion fails silently when storage is full
Functions Timing Out
The default timeout is 150 seconds. For longer operations:
functions:
environment:
FUNCTIONS_TIMEOUT: 300 # 5 minutes
However, consider whether long-running functions are the right approach. Background jobs might be better handled via pg_cron or external job queues.
Deploying Edge Functions Separately
For production workloads, you might want to run Edge Functions on dedicated infrastructure. This gives you:
- Geographic distribution: Run functions closer to your users
- Independent scaling: Scale functions without affecting your database
- Better isolation: Function issues don't impact other Supabase services
Deploying to Fly.io
Supabase provides a demo repository for this exact use case:
# Clone the demo git clone https://github.com/supabase/self-hosted-edge-functions-demo cd self-hosted-edge-functions-demo # Copy your functions cp -r /path/to/your/functions/* ./functions/ # Deploy to Fly.io fly launch fly deploy
Your fly.toml should include:
[env] SUPABASE_URL = "https://your-self-hosted-supabase.com" [experimental] cmd = ["start", "--main-service", "/usr/services"]
Set secrets via Fly CLI:
fly secrets set JWT_SECRET=your-jwt-secret fly secrets set SUPABASE_SERVICE_ROLE_KEY=your-service-key
Connecting to Your Self-Hosted Instance
Whether deploying to Fly.io, Railway, or elsewhere, your Edge Functions need to connect back to your Supabase instance. Ensure:
- Network connectivity: Functions can reach your Supabase Kong gateway
- Correct URLs: Use internal URLs if possible, public URLs otherwise
- SSL certificates: If using custom domains, ensure certificates are valid
Scheduled Functions (Cron)
Unlike Supabase Cloud where you can schedule functions through the dashboard, self-hosted requires manual cron setup.
Option 1: pg_cron Extension
Use PostgreSQL's built-in cron capabilities:
-- Enable the extension
create extension if not exists pg_cron;
-- Schedule a function call every hour
select cron.schedule(
'hourly-cleanup',
'0 * * * *',
$$
select net.http_post(
url := 'http://functions:9000/functions/v1/cleanup',
headers := '{"Authorization": "Bearer YOUR_SERVICE_ROLE_KEY"}'::jsonb
);
$$
);
Option 2: External Cron Service
Use a dedicated cron service like:
- Cron-job.org (free tier available)
- GitHub Actions scheduled workflows
- Your server's crontab
# crontab -e 0 * * * * curl -X POST 'https://your-supabase.com/functions/v1/cleanup' \ -H "Authorization: Bearer SERVICE_ROLE_KEY"
Production Best Practices
1. Always Pin Runtime Versions
Never use latest tags in production:
functions: image: supabase/edge-runtime:v1.69.28 # Specific version
2. Implement Health Checks
Add a health check endpoint to catch issues early:
functions:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/health"]
interval: 30s
timeout: 10s
retries: 3
3. Set Resource Limits
Prevent runaway functions from consuming all resources:
functions:
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
4. Use Environment-Specific Secrets
Never hardcode secrets. Use Docker secrets or environment files:
functions:
secrets:
- jwt_secret
- service_role_key
5. Monitor Function Performance
Set up monitoring to track:
- Invocation counts
- Error rates
- Response times
- Memory usage
Tools like Prometheus and Grafana work well for this.
When to Use Edge Functions vs. Alternatives
Edge Functions are great for:
- Webhook handlers: Processing Stripe, GitHub, or other webhook events
- API proxies: Adding authentication or transforming external API responses
- Light computation: Quick data transformations or validations
Consider alternatives for:
- Heavy computation: Use dedicated workers or background job systems
- Long-running tasks: pg_cron with database functions might be simpler
- Complex business logic: A dedicated API server gives you more control
Simplifying Edge Function Management
Managing Edge Functions on self-hosted Supabase requires more manual work than the cloud version. Supascale can help by providing:
- A unified interface for managing functions across multiple projects
- Simplified deployment workflows
- Integrated monitoring and logging
Check our pricing page to see how Supascale handles the operational complexity of self-hosted Supabase.
Conclusion
Self-hosted Edge Functions are absolutely viable, but they require more configuration and maintenance than the cloud version. The key is understanding the architecture, staying on stable runtime versions, and having good monitoring in place.
Start with the bundled Docker setup, get comfortable with the basics, then consider separate deployment for production workloads that need geographic distribution or independent scaling.
