Introduction
You know that moment when you look at your credit card statement and realize your hobby project is costing you more than your streaming subscriptions combined? Yeah, that was me staring at my DigitalOcean bill.
$12 a month. For one project. The absolute bare minimum setup – a 512 MB RAM instance and a tiny database. And before you ask: yes, that’s actually DigitalOcean’s cheapest App Platform plan. Want to run a cron job without your app wheezing? That’ll be another $5, thank you very much!
because of course you have multiple side projects
Don’t get me wrong – DigitalOcean’s App Platform is solid. It handles all the annoying bits like port configuration, it deploys reliably, and you don’t have to think about infrastructure. But when you’re a hobbyist with multiple side projects (because of course you have multiple side projects), the math gets painful fast. Three projects? That’s $36 each month before you’ve even added any real features.
So I did what any reasonable developer would do: I went hunting for alternatives that wouldn’t require me to justify my hobby spending to my partner.
Enter the world of self-hosted Platform-as-a-Service tools. Specifically: Coolify and Dokploy.
What Are We Even Comparing?
If you’re not already familiar with the self-hosted PaaS landscape (and why would you be? It’s a pretty niche rabbit hole), here’s the quick version:
Coolify and Dokploy are both open-source platforms that turn your regular VPS into something that feels like Heroku or Vercel. You point them at your docker compose files or Git repos, click deploy, and your app just… works. No more ssh-ing into servers at 2 AM. No more manually running docker compose commands. Just a nice UI and automated deployments.
They’re built for developers who want the convenience of a PaaS without the platform pricing. Think indie hackers, hobbyists, and small teams who are comfortable with Docker but don’t want to manage everything manually.
What I Actually Tested
I didn’t just kick the tires with a hello world app and call it a day. I threw real projects at both platforms:
- Python web apps with connected databases (PostgreSQL, the usual suspects)
- blik360.com – my open source 360 performance tool (because if it can’t handle my side projects, what’s the point?)
- One-click templates like n8n (workflow automation)
- Various services that needed memcached, Redis, and other supporting infrastructure
You know, the kind of messy, real-world setups that actually tell you whether a platform plays nice with your existing Docker setups or requires you to bend your projects to fit its opinions.
The Testing Ground
I spun up a mid-range Hetzner server (CPX31) in Finland (€13/month + €0.50 for an IPv4 address – we’ll come back to this pricing later). First, I installed Coolify and spent a few weeks with it. Single containers? Those deployed without much fuss. But my docker compose projects – the ones with multiple services talking to each other – that’s where things got sticky. Then I wiped the server, installed Dokploy, and have been running with it for about two months now.
For context: I’d been using DigitalOcean’s App Platform for over a year before this, so I had a pretty good baseline for comparison – even if that baseline was expensive.

First Stop: Coolify
Let me start with the good stuff: Coolify’s initial setup is actually really nice. The installation was straightforward, the UI is clean and modern, and getting my first few containers deployed felt… easy? Honestly, for single-container deployments, it was pretty smooth sailing.
But then I started throwing my docker compose projects at it.
When Things Got Complicated
Here’s where my experience diverged from the simple happy path. Projects with multiple services – you know, the ones where your app needs to talk to PostgreSQL, Redis is doing some caching, and maybe memcached is in the mix – those just wouldn’t deploy consistently.
And when they failed? The error messages were… let’s call them “cryptically vague.” I’d get deployment failures without much insight into why things weren’t working. Was it networking? Port conflicts? Service discovery issues? Your guess was as good as mine.
I spent multiple evenings debugging, tweaking compose files, trying different configurations. Eventually, I’d get frustrated enough to abandon the server for a bit and come back later with fresh eyes. Rinse and repeat.

What I Think Was Happening
Looking back, I suspect there were subtle differences in how Coolify sets up and runs containers compared to my “default” docker compose setups. Port configurations seemed to be a sticking point, though I couldn’t nail down exactly what Coolify expected versus what my compose files were providing.
The Important Caveat
Now, before this sounds like I’m dunking on Coolify: I know there are lots of happy Coolify users out there. The project has a solid community, and plenty of people are running it successfully in production. My issues were likely a combination of my specific setup, my compose file configurations, and maybe just not clicking with how Coolify wanted things structured.
I love that Coolify just presents you with these lists of open source/self-host options right from the get-go.

But after a few weeks of friction, I decided to try something else.
Enter Dokploy: The Plot Twist
I’m not going to pretend this was some methodical, well-researched decision. I stumbled onto Dokploy basically by accident – probably a random GitHub rabbit hole or an HN thread I can’t even remember now.
But I figured: what the hell, the Coolify server is already frustrating me. Let me wipe it and give this other thing a shot.
Wait, It Actually Works?
Here’s where things got interesting.
I deployed blik360.com – a 360 review tool which refused to cooperate with Coolify – and it just… worked. First try. No debugging. No cryptic errors. Just a successful deployment.
Then I threw another docker compose project at it. Also worked.
Installed the n8n template. Worked like a breeze.
I’m not saying I didn’t have to make any changes – I did need to commit a few tweaks to port configurations for things like memcached and Django’s default ports. But we’re talking minor adjustments, not evening-long debugging sessions.
Speed and Stability That Surprised Me
Okay, this is where Dokploy actually blew my mind a little.
On DigitalOcean, deployments for the same project would take about 3 minutes. Every single time. You’d push code, go grab coffee, come back, and maybe it was done.
On Dokploy? Incremental deployments took about 5 seconds. Full rebuilds? Around 40 seconds.
The first time it happened, I genuinely thought something had broken. I switched from my terminal to the web interface expecting to see it still building, and nope – already deployed and running. It was almost unsettling how fast it was.
A Clean and Opinionated UI
I mean that in a good way. The Dokploy interface is seriously nice to use. It’s clean, intuitive, and – here’s something I really appreciate – it actively encourages you to set up backups. It’s not just giving you the tools and leaving you to figure it out; it’s nudging you toward good practices.
I’ve had multiple automatic upgrades over the past two months, and not once has anything broken. For a self-hosted platform that’s updating itself? That’s kind of impressive.

Features I Haven’t Even Used Yet
There are a couple of things I’m excited to try but haven’t gotten around to:
- Preview environments for pull requests – This could be a game-changer for testing before merging
- Built-in cronjobs – Right now I’m still running crons in separate containers like a caveman
The fact that these features exist and I’m looking forward to using them says something about how comfortable I’ve gotten with the platform.
The Rough Edges: Nothing’s Perfect
Look, I’m clearly team Dokploy at this point, but let’s not pretend it’s flawless. I hit some bumps too.
The WAF Networking Disaster
I wanted to experiment with adding a Web Application Firewall to one of my projects. Seemed reasonable, right?
Wrong.
I started creating, removing, and renaming docker compose networks to get things wired up correctly, and it completely broke the project’s configuration. Like, catastrophically broke it. I ended up having to rebuild the whole thing from scratch.
Now, was this entirely Dokploy’s fault? Probably not – docker networking can be finicky at the best of times. But the platform didn’t exactly help me understand what went wrong or how to fix it gracefully.
The Missing Port Overview
Here’s a feature I really wish existed: a global overview of which services are running on which ports across all projects.
Because here’s the thing about Docker – you can’t have containers binding to the same host port. It’s usually the first issue I run into when deploying a new project. Something fails, I dig into the logs, and oh right, port 5432 is already taken by that PostgreSQL container from three projects ago.
It’d be incredibly handy to have a dashboard showing “Port 5432: Project A’s database, Port 6379: Project B’s Redis” etc. Right now, you kind of have to remember or dig through each project individually.
Multiple Environments: Overkill for Hobbyists?
The interface supports multiple environments per project, which is great if you’re running staging/production setups. But for hobby projects? It feels a bit… much? The UI gets a little vague when dealing with these environments, and honestly, most of my side projects don’t need that level of sophistication.
This is a minor complaint though – it’s not in the way, it’s just extra complexity I’m not using.
Still Worth It
These rough edges are real, but they’re also the kind of things I can work around. None of them are dealbreakers, and honestly, compared to the friction I had with Coolify or the cost of DigitalOcean, they barely register.

Docker Compose Best Practices: Why Your Ports Keep Breaking
Alright, story time is over. Let’s talk about the actual lessons I learned from deploying the same projects across three different platforms.
The TL;DR? Port configuration will ruin your day if you’re not careful.
Hardcoded Ports Are the Root of all Evil
Here’s a classic mistake (that I definitely made): hardcoding ports in your docker compose files. It works fine on your local machine, but the moment you deploy to a platform like Coolify or Dokploy, things start breaking in mysterious ways.
Here’s what a typical “it works on my machine” compose file looks like:
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
depends_on:
- db
db:
image: postgres:15
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=supersecret
See the problem? You’re binding directly to host ports 8000 and 5432. Deploy a second project that also wants port 5432? Boom. Conflict.
Let the Platform Handle Networking
Here’s the better approach – only expose the ports that actually need external access, and let Docker’s internal networking handle service-to-service communication:
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000" # Only expose what needs to be public
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
depends_on:
- db
db:
image: postgres:15
# No ports exposed to host!
environment:
- POSTGRES_PASSWORD=supersecret
Your web app can still talk to the database just fine using db:5432 because they’re on the same Docker network. But you’re not cluttering up your host ports with services that don’t need external access.
Pro Tip: Use Environment Variables
Want to get really flexible? Make your ports configurable:
version: '3.8'
services:
web:
build: .
ports:
- "${WEB_PORT:-8000}:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
Now you can override WEB_PORT per deployment without touching the compose file. Project A runs on 8000, Project B on 8001, and they coexist peacefully.
Application-Level Port Configuration
Don’t forget: your application itself might be hardcoded to listen on specific ports. I had to make commits to fix things like:
- Django’s default
runserver 0.0.0.0:8000 - Memcached always wanting port
11211 - Custom Flask apps bound to specific ports
Make sure your app reads its port from an environment variable:
# Bad
app.run(host='0.0.0.0', port=8000)
# Good
import os
port = int(os.getenv('PORT', 8000))
app.run(host='0.0.0.0', port=port)
Docker Networks: Tread Carefully
Both Coolify and Dokploy create Docker networks for your services. Usually, you don’t need to mess with this – the default networks work fine.
But if you do need custom networking (like I did with my WAF experiment), be very careful. Renaming or removing networks while services are running can leave things in a weird state. My advice? If you need custom networks, plan them upfront rather than retrofitting them later.
Quick Checklist Before You Deploy
- Are you only exposing ports that need external access?
- Are service-to-service connections using internal DNS names (e.g.,
db:5432)? - Can your app’s listening port be configured via environment variable?
- Have you checked for port conflicts with other projects on the same host?
- Are sensitive values (passwords, secrets) using environment variables instead of being hardcoded?
Get these right, and you’ll save yourself hours of debugging.
The Features That Actually Excited Me
One of the best signs that a platform is working well? When you start getting excited about features you haven’t even used yet.
Preview Environments for Pull Requests
This is a feature I keep meaning to set up but haven’t gotten around to yet. Here’s the idea: you open a PR, and Dokploy automatically spins up a preview environment with those changes. You can test, show it to someone, break things without consequences, and then tear it down when you merge.
Now, this isn’t exactly a revolutionary feature – it’s pretty common in self-hosted PaaS platforms these days. But having it available and ready to use when I need it? That’s nice. I just haven’t had a project yet where I’ve needed it enough to actually configure it.
For solo hobby projects, it’s probably overkill. But for anything where you’re collaborating or want to test changes before they hit production, it’s good to know it’s there.
Built-In Cronjobs
Right now, I’m running crons the old-fashioned way – separate containers that wake up, do their thing, and go back to sleep. It works, but it’s not exactly elegant.
Dokploy has built-in cronjob support. You can define scheduled tasks right in the UI, and they run in the context of your application. No extra containers, no hacky scripts, just “run this command on this schedule.”
I haven’t migrated my existing crons over yet, but it’s on my to-do list. Mostly because the current setup works and I’m lazy, not because there’s anything wrong with Dokploy’s approach.
Auto-Updates That Don’t Break Things
Here’s something I didn’t expect to appreciate as much as I do: Dokploy updates itself automatically, and in two months, not a single update has broken anything.
I know that sounds like a low bar, but have you ever run self-hosted software that updates itself? It’s usually terrifying. You wake up to find the platform upgraded overnight and now half your projects won’t deploy because some dependency changed.
With Dokploy? I’ve had multiple updates roll through, and everything just keeps working. Either I’m incredibly lucky, or they’re doing something right with their release process.
Backup Encouragement
This isn’t exactly a flashy feature, but the UI actively encourages you to set up backups for your databases and volumes. It’s not buried in some advanced settings menu – it’s right there, front and center.
For hobby projects, it’s easy to skip backups because “eh, it’s just a side project.” But when your side project is something you’ve been working on for months (or years), losing data because you didn’t set up backups feels pretty stupid in hindsight.
Dokploy makes it easy to not be stupid.
The Money Shot: Cost Comparison
Alright, let’s talk about what really matters: money.
Monthly cost comparison
DigitalOcean App Platform: Death by a Thousand Cuts
Remember that $12/month I mentioned at the beginning? Let’s break down what you actually get for that:
- $5/month for the cheapest app instance (512 MB RAM – yes, really)
- $7/month for a managed database (because your app probably needs to store data)
- $12/month total for ONE project at the absolute minimum specs
And here’s the kicker: that 512MB instance? It’s not enough for anything real. I had one project where running the app + crons + caching was too much for that tiny server. My options were:
- Pay another $5/month just to run cronjobs separately
- Upgrade to a beefier instance
- Feel bad about my life choices
I chose option 3, followed eventually by “find a different platform.”
Oh, and if you have multiple projects? That $12 multiplies per project. Three hobby projects? You’re at $36/month. Five projects? $60/month. For hobby projects. That you’re probably making zero money from.
The Self-Hosted Alternative: Hetzner + Dokploy
Here’s what I’m running now:
- €13/month for a mid-range Hetzner server in Finland
- €0.50/month for an IPv4 address
- €13.50/month total (~$14.50 USD)
And on this single server? I’m running five different projects right now. Five. With room to spare.
The same workload on DigitalOcean would cost me $60/month. I’m paying $14.50. That’s a 75% cost reduction.
Quick Maths
The Catch: There’s Always a Catch
Self-hosting isn’t free in terms of effort. You’re responsible for:
- Server maintenance and updates (though Dokploy handles most of this)
- Backups (set them up once, but you need to remember)
- Monitoring and uptime (no support team to call at 3 AM)
- Security patches and configuration
For DigitalOcean, you’re paying for someone else to worry about all of that. For self-hosting, you’re trading money for a bit of your time.
But honestly? With Dokploy auto-updating itself and the deployment process being this smooth, the maintenance burden is pretty minimal. I spend maybe an hour a month thinking about server stuff, and most of that is deploying new projects, not firefighting.
When DigitalOcean Actually Makes Sense
Let me be fair here: there are cases where DigitalOcean’s App Platform (or similar managed services) are worth the premium:
- You’re making money from your projects and your time is literally worth more than $50/month
- You need guaranteed uptime and professional support
- You’re not comfortable with Docker and self-hosting
- Your projects have variable/spiky traffic and you need easy autoscaling
But for hobby projects? Side hustles? Learning projects? The math is pretty hard to argue with.
So Which One Should You Choose?
Look, I’ve made my preference pretty clear by this point. I’m running Dokploy, I’m happy with it, and I’m planning to migrate my remaining projects over. But that doesn’t mean it’s the right choice for everyone.
When Coolify Might Be Right for You
Despite my frustrations, Coolify has a large and happy user base for good reasons:
- You’re deploying single containers – If your projects are mostly standalone containers without complex compose setups, Coolify works great
- You’re already running it successfully – If you’ve already invested time learning Coolify’s quirks and your projects are running fine, there’s no reason to switch
- The community and documentation – Coolify has been around longer and has more resources available
If your projects are deploying successfully on Coolify and you’re not running into issues, honestly, just stay there. Don’t fix what isn’t broken.
Why Dokploy Won For Me
Here’s what tipped the scales:
docker composejust worked – My existing compose files deployed with minimal tweaking- Speed – Those 5-second deployments are genuinely delightful
- Stability – Two months of auto-updates without a single issue
- The UI encourages good practices – Backups front and center, clean configuration
- It got out of my way – I spend more time building projects than fighting the platform
For my specific use case – multiple Python apps with databases, real-world compose files, hobby projects that need to be cost-effective – Dokploy has been the clear winner.
The Real Question: Is Self-Hosting Worth It?
Both Coolify and Dokploy require you to be comfortable with the following:
- You understand Docker fundamentals – Not an expert, but you know what a container is and how compose files work
- You’re okay with some DIY – When things break, you’re the support team
- You can handle basic Linux admin tasks – SSH, file permissions, that sort of thing
- You’re willing to set up monitoring and backups – Because no one else will do it for you
If that sounds exhausting, stick with managed platforms like DigitalOcean, Render, or Railway. They’re more expensive, but your time has value too.
But if you’re already comfortable with that stuff? The cost savings and flexibility of self-hosting are pretty compelling.
My Recommendation
If you’re choosing between Coolify and Dokploy today:
- Try Dokploy first – Especially if you have
docker composeprojects. See if they deploy easily. - If Dokploy doesn’t click, try Coolify – Different platforms work better for different people and projects
- Give each one a fair shot – Deploy a real project, not just a hello-world app
- Don’t overthink it – Both are free and open-source. You can always switch if something isn’t working
The beautiful thing about self-hosting? You’re not locked into contracts or migrations. Spin up a server, try it out, and if it doesn’t work, wipe it and try something else.
Final Thoughts
So here’s where I’m at: running five projects on a single Hetzner server with Dokploy, saving about $45/month compared to my old DigitalOcean setup, and actually enjoying the deployment process instead of dreading it.
I’ve still got one last project sitting on DigitalOcean that I need to migrate over. It’s not because it’s complicated – I’m just strapped for time. But knowing I can move it whenever I’m ready without having to worry about whether it’ll deploy correctly? That’s a nice feeling.
What I’ve Learned
The self-hosted PaaS space is genuinely good now. Not good for self-hosted with a bunch of caveats – actually good. The tools have matured to the point where you can get most of the convenience of managed platforms without the platform pricing.
Your mileage will vary based on your specific projects and compose file setups. What worked (or didn’t work) for me might be completely different from your experience. But the barrier to trying is low: spin up a cheap VPS, install one of these platforms, and see what happens.
Your Turn
I’d love to hear about your experiences with Dokploy, Coolify, or other self-hosted PaaS tools. What worked? What broke? What made you want to throw your laptop out the window?
Are you still on managed platforms? Thinking about self-hosting? Already running your own setup with a completely different tool?

Thijs is an entrepreneur and software developer who started building websites at age 9.
With over 15 years in tech and a decade in content delivery, he focuses on product development, architecture, and security.
He believes technology should feel intuitive, not complicated, and builds scalable, human-centered solutions that make a real impact.
Outside of work, he enjoys reading everything from business strategy to science fiction.





