The .self Domain Migration: Why I'm Moving My Entire Stack to Self-Hosted

Three months ago, I registered moygulati.self and made what felt like a simple decision: migrate everything I build to this domain. But it wasn't just a domain change. It became a complete infrastructure experiment—rebuilding my entire development stack with zero external dependencies, using the .self TLD as both a technical choice and a philosophical statement about truly owning your digital infrastructure.
The .self top-level domain exists specifically to support self-hosting. Unlike .com or .org, it's designed for people running their own servers, managing their own DNS, and taking full responsibility for their digital presence. What I didn't anticipate was how this migration would force me to confront every assumption I'd made about building on the internet.
The Dependency Audit That Started Everything
Before the migration, I did what any reasonable developer does: I counted my dependencies. The results were unsettling. My supposedly "simple" personal projects were spread across six different companies: GitHub Pages for hosting, Netlify for CI/CD, Vercel for preview deployments, Auth0 for authentication, PlanetScale for databases, and Stripe for payment processing.
Each service handled one piece beautifully, but together they created a web of external dependencies that I'd stopped noticing. When my site went down, I had to check six different status pages. When something broke, I was filing support tickets instead of fixing code. I wasn't building infrastructure—I was assembling other people's infrastructure and calling it mine.
The realization hit me: I'd been renting my digital existence, not owning it.
Month One: The Technical Reality Check
Moving to .self domains immediately surfaced technical quirks that nobody mentions in the self-hosting guides. DNS propagation takes longer than the usual 15 minutes, because .self domains work differently than standard TLDs. Some DNS resolvers don't handle them properly yet, which means some users simply can't reach your site.
Certificate provisioning became its own adventure. Let's Encrypt's automation, which works flawlessly with traditional domains, breaks in subtle ways with .self. I spent two days debugging certificate renewal scripts that I'd never had to think about before.
Email routing was worse. Most email providers flag .self domains as suspicious by default. My carefully crafted transactional emails were landing in spam folders, and there's no support team to call when Mailgun decides your domain looks sketchy.
Then came my first real server crash at 2 AM. The disk filled up during a routine backup, taking down everything I'd built. No status page, no incident report, no team of engineers working to restore service. Just me, a terminal, and the stark realization that I couldn't blame anyone else.
Architecture Decisions You Can't Outsource
Moving off platforms forced me to make architectural decisions I'd been avoiding for years. Database backups became my problem—no more relying on PlanetScale's automated snapshots. I had to design backup strategies, test restore procedures, and accept that data loss was now entirely my responsibility.
Authentication without Auth0 meant building login flows from scratch. No more OAuth integrations that "just work." I'm hand-rolling JWT tokens, managing session storage, and dealing with password reset flows that I used to implement with a few lines of SDK code.
Payment processing got complicated fast. Stripe's hosted checkout pages and automated webhook handling had been invisible magic. Now I'm writing webhook validation, handling payment state reconciliation, and debugging failed transactions without Stripe's dashboard holding my hand.
Even CDN replacement became a puzzle. Replacing Cloudflare's global edge network with nginx optimization and careful caching strategies works, but it requires understanding how HTTP headers actually work instead of just toggling settings in a web interface.
The Psychology of Complete Ownership
The psychological shift surprised me more than the technical challenges. Platform constraints used to make decisions for me—Netlify's build limits shaped my deployment strategy, Vercel's serverless model influenced my architecture choices. Without those guardrails, I faced decision paralysis in new ways.
But I also discovered something unexpected: blame assignment changes everything when you own every layer. When my site was slow, I used to wonder if it was Netlify, Cloudflare, or my code. Now I know it's my code, my server configuration, or my network setup. The debugging process became archaeology instead of guesswork.
Building for resilience started taking precedence over building for features. When downtime is entirely my responsibility, I write different code. I add more logging, build in graceful degradation, and design for failure modes that I used to assume someone else would handle.
Three Months In: What Changed
My code quality improved in ways I hadn't expected. Knowing that deployment issues will be my problem at 3 AM makes me write more careful deployment scripts. Understanding that database queries hit my server, not some managed service, makes me write more efficient SQL.
Infrastructure choices became driven by understanding rather than marketing pages. I chose PostgreSQL over MongoDB not because of feature comparisons, but because I understand PostgreSQL's failure modes and recovery procedures. I picked nginx over Apache because I've spent time reading nginx configuration documentation.
The mental clarity of simplified dependency graphs was remarkable. My monitoring dashboard now shows actual problems instead of the filtered view that managed services provide. When something breaks, I'm debugging my system, not reverse-engineering someone else's abstractions.
The Economics Nobody Talks About
The financial math isn't straightforward. My dedicated server costs $89/month, which seems expensive compared to free hosting tiers. But I was paying for Auth0, PlanetScale's production features, Netlify's build minutes, and hidden costs in Vercel's function usage. The real cost was never just the hosting.
Time investment was significant—setup and configuration that would have taken minutes with managed services stretched over days. But I'm trading those upfront hours for years of recurring decisions about which services to use, which pricing tiers to choose, and which feature limitations to accept.
Some calculations show self-hosting as more expensive when you factor in time costs. I'm continuing anyway because the expense isn't just financial—it's about control and understanding of my own infrastructure.
Current Stack Reality
My hardware is a dedicated server with 32GB RAM and 1TB NVMe storage, chosen because I understand what my applications actually need rather than guessing at platform tiers. The software stack is nginx for HTTP handling, PostgreSQL for data, custom authentication middleware, and manual deployment scripts that I wrote and maintain.
Monitoring runs through a Grafana dashboard that shows exactly what I need: database connection counts, disk usage, response times, and error rates. No abstracted metrics or feature-limited free tiers—just the data I need to keep things running.
My backup strategy required actual design work. Daily database dumps to encrypted storage, weekly full system snapshots, and quarterly restore tests that I actually run. At 3 AM last month, I was grateful for this paranoia when I needed to restore from a corrupted table.
What other dependencies am I still blind to? The .self migration revealed how much of my infrastructure I'd unconsciously outsourced, but three months in, I suspect there are layers of dependence I haven't even identified yet.