Look, I've deployed Fresh apps to production for the past year, and honestly? It's refreshingly straightforward compared to the Node.js hellscape most of us are used to. Though that's not saying much.
Fresh 2.0 with the new Vite integration literally boots in 8ms now. That's not a typo - they went from 86ms to 8ms boot time on their own website. The "9-12x faster" performance improvements everyone's talking about? That's boot time, not some magical runtime performance boost.
What Actually Works in Production
Here's what I've learned after running Fresh apps that handle ~15k requests per day:
The Good Stuff:
- Deno Deploy "just works": Push to GitHub, it deploys. No webpack configs, no build pipelines that break randomly at 3am
- TypeScript without the pain: No
tsconfig.json
debugging sessions. It just compiles - Islands architecture is legit: Your HTML loads instantly, JavaScript only where you need it. Bundle sizes are tiny
- Edge deployment: Your app runs close to users automatically
The Gotchas Nobody Tells You:
- Environment variables sync slowly: After changing env vars on Deno Deploy, wait 30 seconds before testing. I learned this the hard way
- Cold start reality: 8ms boot time is great, but your database connections still take 100-200ms to establish
- Memory limits: Deno Deploy free tier gives you 128MB. Sounds like a lot until you try to cache everything in memory
- CORS headaches: Safari randomly refuses requests sometimes. Add proper CORS headers in middleware
The Actual Deployment Process
Skip the enterprise architecture diagrams. Here's what you actually do:
Step 1: Push Your Code
## This is literally all you need
git push origin main
That's it. Deno Deploy watches your repo and deploys automatically. No Docker, no CI/CD pipelines, no YAML hell.
Step 2: Set Environment Variables
Go to your Deno Deploy dashboard, click "Settings," add your env vars. They take about 30 seconds to sync.
Step 3: Custom Domain (Optional)
Add your domain in the dashboard. SSL certificates are automatic. No nginx configs, no Let's Encrypt scripts.
What Breaks and How to Fix It
"Module not found" errors on deploy:
Your imports are probably wrong. Deno Deploy is stricter about file extensions:
// Wrong - works locally, breaks on deploy
import { thing } from "."/utils";
// Right - works everywhere
import { thing } from "."/utils.ts";
"Permission denied" errors:
Check your deno.json
- you need the right permissions:
{
"tasks": {
"start": "deno run --allow-net --allow-read --allow-env main.ts"
}
}
Database timeouts:
Connection pooling is your friend. Don't create new connections for every request:
// Wrong - creates connection hell
export async function handler(req: Request) {
const client = new Client(DATABASE_URL);
// ... do stuff
}
// Right - reuse connections
const pool = new Pool({ connectionString: DATABASE_URL, maxConnections: 5 });
Platform Reality Check
Deno Deploy (Recommended):
- Free tier: 100k requests/month, 128MB memory, automatic SSL
- Pro tier: $20/month for 1M requests, 512MB memory, priority support
- Reality: The free tier works for most side projects. You'll hit the memory limit before the request limit
Alternatives that actually work:
- Cloudflare Workers: Faster than Deno Deploy, but 1MB bundle limit will bite you
- Docker: Overkill unless you need custom infrastructure or can't use edge platforms
- Vercel/Netlify: Don't even try. They don't support Deno properly
Performance Tips That Matter
Caching static assets:
// routes/_middleware.ts - Add cache headers
if (url.pathname.startsWith("/static/")) {
const resp = await ctx.next();
resp.headers.set("Cache-Control", "public, max-age=31536000");
return resp;
}
Database connections:
The biggest performance killer is creating new database connections for every request. Use a connection pool:
const pool = new Pool({
connectionString: DATABASE_URL,
maxConnections: 5 // Don't go crazy here
});
Islands optimization:
Keep your islands small. Each island is a separate JavaScript bundle. One big island = slow hydration.
Monitoring That Actually Helps
Forget the fancy APM tools. Start with this:
// Log what matters
console.log(`${req.method} ${url.pathname} - ${resp.status} - ${duration}ms`);
Deno Deploy gives you basic metrics for free. For anything else, log to stdout and use their log viewer. Simple beats complex every time.
Fresh deployment beats wrestling with Docker configs at 3am. Deno Deploy handles the annoying infrastructure shit so you can actually build features instead of debugging YAML files for three hours.