so I was reading https://matt.sh/htmx-is-a-erlang and I thought this quote was really interesting:
[Erlang creator Joe Armstrong] always said his favorite erlang program was the universal server: a process capable of receiving a message to become another server.
I've wondered in the past what it would look like to have a Roc server platform that could:
assuming you have the current basic-webserver API, where the application is nothing more than Request -> Task Response [] (and it holds no state in between requests other than maybe things like caching tcp connections behind the scenes), it seems like upgrading the host could be possible by having a super minimal "bootstrap" host which does nothing more than:
dlclose me")presumably if the application were also loaded dynamically, a similar strategy could work there too
so at that point, the original super minimal bootstrap logic would run forever, and as long as that never needed to be upgraded (which hopefully it wouldn't need to be, given how simple its job would be) I can't think of a reason this couldn't in principle run indefinitely :thinking:
(without ever having downtime)
of course you can already do this in a different way by running multiple server processes, but I think it's interesting to think about! :big_smile:
Yeah, the easy way to do this is to have a load balancer and just take down and upgrade one server at a time. Site as a whole has 100% uptime
Also, the bootstrap process has to make sure no connections drop while upgrading the server. So it would have to maintain a buffer for that.
Oh, NVM. I see that you want both version running dynamically at the same time. Just need to make sure you avoid any sort of port issues
Aside, if roc contains all your application logic, you probably don't really need to upgrade the server base often. So a platform like this that just deals with dynamically loading the roc app would be really cool and probably easy to do (probably would be really nice for quick feedback loop server development as well)
I need this
Well... I mean to say I would really love this to exist
for local development? Production? Both?
for local development I think hot code loading is the ultimate version of this
Yeah mostly for local development.
It currently takes ~5s for roc dev to reboot my webserver. I'm on macos arm64 using llvm. If that could happen instantly on file save it would be awesome.
Hot reloading of modules in erlang/elixir works works pretty great when developing :)
Upgrading a whole application (release) is another story and is generally avoided in favor of blue-green deploys. It quickly gets really complex and comes with a ton of footguns when you have state involved that needs to be migrated between versions.
Hot upgrades is kinda oversold feature in Elixir/beam that most people will never use. But it is amazing that it can be done.
The erlang virtual machine got so many things right IMO (and most of it 30 years ago) and I wish it had a greater influence in the languages we use today. :)
Luke Boswell said:
It currently takes ~5s for
roc devto reboot my webserver. I'm on macos arm64 using llvm. If that could happen instantly on file save it would be awesome.
How much of that time is linking? 2s?
I have dreams about writing an Erlang platform that can do hot upgrades. I know Gleam skipped trying to do hot upgrades, but I hope Roc could do it with sufficiently fancy Erlang/Elixir platform code.
@Brendan Hansknecht I may have been overly dramatic... here is some actual data. I swear though, that sometimes it runs a slower than this, but I can't reproduce right now.
And for an optimized build
Code Generation
829.612 ms Generate final IR from Mono IR
10266.172 ms Generate object
11095.784 ms Total
Finished compilation and code gen in 11435 ms
Produced a app.o file of size 429640
Finished linking in 284 ms
Looks like you really need a full dev backend that supports everything basic webserver might do
so for the non-optimized build, that was:
dev backend and surgical linking: the perf benefits are for real :sweat_smile:
I really hope in 2024 we can get full coverage for dev backend and surgical linking...dropping ~96% of build time would be no joke!
I think @Brendan Hansknecht was right. This is a pretty solved problem using a load balancer of any variety (i think they all support this)
All you would need is the ability in the app to close the listener (stop accepting new connections) while serving out open connections (and generally closing keep-alive idle connections where applicable, since those might stay open a very long time)
If you need the actual app processes to be able to do a direct handoff, at least on unix systems, you'd:
The key is the inherited socket (which works with any fd). In the brief time in which both parent and child processes are accepting connections, the kernel will distribute connections to both processes seamlessly.
Using processes instead of dynload/dynunload means that:
Last updated: Jun 16 2026 at 16:19 UTC