I am working on a FRP web app that runs Purescript on the front end and Haskell (Servant and Opaleye) on the back end talking to a PostgreSQL database.
Right now, PostgreSQL runs natively (I found a flake containing a set of shell scripts from the Elixir discourse that help me start, stop, setup, etc the database) but I can’t help but have the inkling that I’m going about this all wrong.
My instincts say that is far more advisable to do this as a Docker or OCI container (despite my Nixy aversion to those solutions). I don’t think I want to go the route of using postgresql as a NixOS service for the same portability/canonical instincts.
My instincts say that I should instead be creating an OCI image that spins up the database rather than running it natively. Is that true?
Can you point me to a canonical example or some documentation that would set me straight on this type of thing? Obviously I tend to do things using 100% Nix but this one has me a little confused since it feels wrong to use Nix for this type of thing.
Basically, I want my entire dev environment to be provisioned and spun up using that one Nix flake. I’m doing this to not only provision my dev environment anywhere but to also deploy this app easily when that time comes. If someone doesn’t mind steering me straight, I’d be incredibly thankful.
Bonus Issue I’ve been struggling with: My preferred Haskell build tool (IOG’s Haskell.nix) is very broken right now with the recent major changes to postgresql-libpq. I get lots of errors when I run ‘nix-develop’ in the build which refer to pkg-config not knowing where to find anything with the recent changes. I tried setting env variables in the shell hook but that really never worked. So, I just commented it out and use ‘cabal build’ at the moment until I can possibly fix that.
ps. I also have had similar issues with building Purescript with purs-nix and have abandoned it since the new version of spago was launched.
This reply may not answer your question. But hopefully it gives you some useful trains of though to follow when coming up with a solution for yourself.
You posted this just before I was going to try deploying a webapp with NixOS and Docker, so in a week or so after I try do that I can give you more info from my experience.
But anyway a couple of my opinions:
A database is stateful, ugly, and “impure” no matter what. Nix is built around the concept of being able to throw away state and recreate it easily. So when you combine the two it is like putting a square peg in a round hole.
In a situation like this your instincts are right that you should be using another tool like OCI containers. In my NixOS config I have this systemd service which runs an OCI container via Podman (yes docker-compose can use Podman instead of Docker). In this case I start it with a systemd service but of course as a developer you could just run the composeScript it in a shellHook in a devShell.
In your compose.yaml, as long as you specify your container to be in a remote repo, it will just automatically pull it from there and reproducibly run the same startup commands. No need to have any preexisting state on your system. So it is about as declarative as Ansible which, although it is not Nix, is still pretty good.
Here is a sanitized systemd service from my config:
systemd.services.docker-compose-my-service = (
let
composeScript = pkgs.writeScript "compose_script" ''
#!/bin/sh
export PODMAN_COMPOSE_PROVIDER=${pkgs.docker-compose}/bin/docker-compose
${pkgs.podman}/bin/podman compose --file /path/to/compose.yaml up --remove-orphans --force-recreate --detach
'';
in
{
enable = true;
description = "Start my-service via docker-compose";
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "-${composeScript}";
};
}
);
Thanks Zach! I’m loving the variety of opinions I’ve gotten so far on this. I cross-posted this question to the Purescript and Haskell discourses and got a good reply on the Haskell one about copying IHP which seems to have chosen to run it as a NixOS service.
In my NixOS config I have this systemd service which runs an OCI container via Podman (yes docker-compose can use Podman instead of Docker). In this case I start it with a systemd service but of course as a developer you could just run the composeScript it in a shellHook in a devShell.
In your compose.yaml, as long as you specify your container to be in a remote repo, it will just automatically pull it from there and reproducibly run the same startup commands. No need to have any preexisting state on your system. So it is about as declarative as Ansible which, although it is not Nix, is still pretty good.
I’m now looking directly at this comment when I think through going the OCI route. I may have a few things up my sleeve about drawing those environment variables from the config file I made.
I’ve gotten a lot better at passing values around the Nix structure recently, so I figure why not? I could build some Nixy declarations into it and perhaps dynamically derive parts of that compose.yaml during the shell hook perhaps. The more I think about it, the worse it sounds. a chicken or egg problem. “If you create the compose file who creates the compose file if you can’t be there, Nix?”
I’m soon going to embarking on a similar route (I think I’ll probably go with Hyperbole or Miso instead of purescript though) and I plan on running my application natively with nixos services but you might also be interested in Services Flake:
I haven’t personally used it but it seems to fit the feature set you’re looking for.