with stack the way to deal with docker is somewhat straightforward; make an image with stack and buidl dependencies all installed, build the project, make another image with runtime deps installed, copy the binary from original to runtime, deploy runtime image
but that all feels so much more confusing/unclear in a nix world; do i really need to build an image with nix and ALL of that installed? haskell images are already giant (like 10 gb is not uncommon), is it sensible to add nix on top of that? then what do i copy over to the runtime image? ok the executable, but what about the runtime deps? do i have to copy all of them? I guess they have to match the directory structure of the build environment too?
it is definitely helpful to use justStaticExecutables to cut down on size, and then you'll run into problems with native libs.
I've got a very well working setup with static-haskell-nix though, reducing image size from about 1G to 20M
At work we use stack inside of a docker container. Then we have a script we run that loops rebuilding our main app and a few external services with stack build --fast , copies them to the correct location, and restarts the docker containers.
I haven't found better solution for development in Haskell (I'm using VSCode) besides ghcid. Maybe I'll try HLS in the next future. I'm not sure if I want to use stack or cabal in my development flow with haskell.nix
So imagine this scenario and compare stack vs nix:
Application foo
Service bar
DB baz
With stack you can use stack in a docker container and docker containers for the other service and DB too. This works alright, but breakages tend to happen when upgrading, or at least way more rebuilding than you might like.
With nix you can do:
avoid docker containers entirely and use stack. Use nix to bootstrap packages in GHC_PATH and make sure stack follows this
build each of the docker containers with nix (maybe even using Arion?)
Option 2 seems nice, but it's much slower than the docker equivalent because nix-build is slower than stack build --fast. I'm betting that (but hope I'm wrong about) stack build --copy-bins && PUT /request/to/restart-docker is faster than nix-build with option 2 and restarting the freshly built docker containers.
I think most people use option 1, but use Cabal instead of stack. Cabal didn't seem to ever ignore GHC_PATH, but with option 1 if you call a stack command before an environment is rebuilt with lorri, stack will take it upon itself to install the packages in your .stack-work directory.
ALL of that said, this is kind of besides the point I wanted this thread to serve :laughing:
Not that I mind, since other questions I have are also being answered :smile:
I actually created this because @Sridhar Ratnakumar said in the thread about "what would you change about Haskell" that stack should replace nix. This made me wonder what real world advantages people have had using nix over stack. The kind of advantages that you could use to convince a die-hard stack lover that nix is worth it for instance :wink:
I haven't found better solution for development in Haskell (I'm using VSCode) besides ghcid. Maybe I'll try HLS in the next future. I'm not sure if I want to use stack or cabal in my development flow with haskell.nix
So this is one big advantage of nix over stack I keep in mind as well:
developers locally can build something during the course of regular development and that build gets pushed to cache
CI runs nix-build-uncached and no build needs done since we can go by the guarantee of the developer having built it locally
Nice to know that. I never use stack, so this is interesting. For me, I usually use plain haskellPackages, then doing nix-shell and use cabal instead of stack.
Recently, I use more of nix flakes, and the development speed is really fast (I'm using rust and go in my day to day work). I want to start some haskell project next month, so I wonder how I can have good setup for haskell in nix.
what IDE do you use? I'm using VSCode, so I wonder this https://github.com/xtruder/debian-nix-devcontainer? will saved my time. The idea is hooking up that, with stack available in my container, and spin up the project with VSCode and docker inside of it. It is still raw idea, but from your saying, I think I want to try stack at least for once.
codygman could you clarify why you're running the app you're hacking on in a docker?
I don't think it strictly has to be and that's just how things are currently.
I've considered just running the haskell application and haskell service locally. However there is also in the real world example (not the one I gave) another quite large javascript application with many functions including populating one of our databases.
Further I've considered replacing our bash script that restarts all of those services to have be a haskell function and then using ghcid to do this like ghcid --test=restartApplicationAndService or something.
I'm not sure what the alternative would be to have a reasonable change compile cycle. Not sure if you are implying there might be a better alternative.
well obviously I don't know your requirements but for building features I would strongly prefer smaller environments with in-memory variants of service dependencies
neuron uses nix+cabal, with ghcide as well as HLS (run nix-shell --run 'code .' for a ready dev environment). there is docker.nix that builds docker image by copying over nix-build's binary (weights ~150mb). no need for stack or docker, really. use nixos for multi service deploy.
I've been moving between pure stack, stack --nix, stack inside of a nix shell, and cabal in a nix shell.
I'm curious what others see the advantages of any of the nix variants above are over just using stack.
One recent real world example:
Different versions of tinfo6 we're used with ghc based on stack.yaml settings changing that caused spurious rebuilds in our dev docker containers
when I'm developing, I only use
ghcid
. then to ensure the health of the whole project, I do anix-build
what tasks do you perform and how do they differ for your several variants?
At work we have a more complicated piece that rebuild binaries, copies into an exposed volume, and restarts docker containers
I'm thinking about moving that into nix build but I'm not sure how tests with external services are usually handled in nix expressions
stack handles docker?
I think it does in some way (that isnt actually useful) but i interpret cody's comments somewhat differently
with stack the way to deal with docker is somewhat straightforward; make an image with stack and buidl dependencies all installed, build the project, make another image with runtime deps installed, copy the binary from original to runtime, deploy runtime image
but that all feels so much more confusing/unclear in a nix world; do i really need to build an image with nix and ALL of that installed? haskell images are already giant (like 10 gb is not uncommon), is it sensible to add nix on top of that? then what do i copy over to the runtime image? ok the executable, but what about the runtime deps? do i have to copy all of them? I guess they have to match the directory structure of the build environment too?
with nix there are also pretty useful tools available:
and then do i have to actually install the nix tooling to get things to work right when I run my application?
ahh cool
(fwiw I have done zero research on it, this is just where my brain went when reading cody's comment)
it is definitely helpful to use
justStaticExecutables
to cut down on size, and then you'll run into problems with native libs.I've got a very well working setup with
static-haskell-nix
though, reducing image size from about 1G to 20MJoel McCracken said:
well you always need to install something if you run your app with your build tool
right well what i mean is do i need to run it inside nix-shell because of various environment variable setup stuff
I'd say that exactly this is the intended purpose of nix :smile:
so the answer is that i can't just copy the binary over to the bare image, i need to set up nix tooling on the runtime image
not quite sure I understand the scenario. in my example, nix builds the image that contains nothing except for the static app binary
so how do you run that binary?
aren't we talking about docker?
there's just a native binary, that you put into
ENTRYPOINT
haha sorry. so there is a command in a docker image; its either specified in the dockerfile (using CMD IIRC) or you can change it with the run command
no deps, glibc and everything linked in
ok so you don't HAVE to do
nix-shell --foo '/path/to/asdoifjaisdof9899/static-binary'
you'd just go to
/prod/static-binary
no, in my case I just use a naked alpine with a blackbox ELF executable
cool
that's great to know, thanks
let me know if you want more information!
Torsten Schmits said:
At work we use stack inside of a docker container. Then we have a script we run that loops rebuilding our main app and a few external services with stack build --fast , copies them to the correct location, and restarts the docker containers.
You can do interesting part with Nix, e.g https://twitter.com/zimbatm/status/1309059217849024513
It's now down to 59s thanks to the latest updates on the @cachix_org installer :-) https://twitter.com/zimbatm/status/1298978045974388736 https://twitter.com/zimbatm/status/1309059217849024513/photo/1
- $ zimbatm 🔨 (@zimbatm)I haven't found better solution for development in Haskell (I'm using VSCode) besides ghcid. Maybe I'll try HLS in the next future. I'm not sure if I want to use stack or cabal in my development flow with haskell.nix
So imagine this scenario and compare stack vs nix:
Application foo
Service bar
DB baz
With stack you can use stack in a docker container and docker containers for the other service and DB too. This works alright, but breakages tend to happen when upgrading, or at least way more rebuilding than you might like.
With nix you can do:
Option 2 seems nice, but it's much slower than the docker equivalent because nix-build is slower than
stack build --fast
. I'm betting that (but hope I'm wrong about)stack build --copy-bins && PUT /request/to/restart-docker
is faster thannix-build
with option 2 and restarting the freshly built docker containers.I think most people use option 1, but use Cabal instead of stack. Cabal didn't seem to ever ignore GHC_PATH, but with option 1 if you call a stack command before an environment is rebuilt with lorri, stack will take it upon itself to install the packages in your .stack-work directory.
Hopefully all of this makes sense.
ALL of that said, this is kind of besides the point I wanted this thread to serve :laughing:
Not that I mind, since other questions I have are also being answered :smile:
I actually created this because @Sridhar Ratnakumar said in the thread about "what would you change about Haskell" that stack should replace nix. This made me wonder what real world advantages people have had using nix over stack. The kind of advantages that you could use to convince a die-hard stack lover that nix is worth it for instance :wink:
Rizary said:
So this is one big advantage of nix over stack I keep in mind as well:
Nice to know that. I never use stack, so this is interesting. For me, I usually use plain
haskellPackages
, then doing nix-shell and use cabal instead of stack.Recently, I use more of nix flakes, and the development speed is really fast (I'm using rust and go in my day to day work). I want to start some haskell project next month, so I wonder how I can have good setup for haskell in nix.
what IDE do you use? I'm using VSCode, so I wonder this https://github.com/xtruder/debian-nix-devcontainer? will saved my time. The idea is hooking up that, with stack available in my container, and spin up the project with VSCode and docker inside of it. It is still raw idea, but from your saying, I think I want to try stack at least for once.
@codygman could you clarify why you're running the app you're hacking on in a docker?
Torsten Schmits said:
I don't think it strictly has to be and that's just how things are currently.
I've considered just running the haskell application and haskell service locally. However there is also in the real world example (not the one I gave) another quite large javascript application with many functions including populating one of our databases.
Further I've considered replacing our bash script that restarts all of those services to have be a haskell function and then using ghcid to do this like
ghcid --test=restartApplicationAndService
or something.I'm not sure the best way forward though.
so your app and service are part of the same compilation unit and you restart them both after building?
Torsten Schmits said:
Yes, exactly.
sounds rather cumbersome!
have you thought about running two
ghcid
s at the same time?Hm, I haven't looked at that... no. I think the services change infrequently enough for it not to be a concern though.
oh, multiple services? like, you're running the entire company infrastructure on your dev machine? :upside_down:
Torsten Schmits said:
Yep, that's correct entire infra over about 4-5 docker containers :big_smile:
:laughing: sounds serious
I'm not sure what the alternative would be to have a reasonable change compile cycle. Not sure if you are implying there might be a better alternative.
well obviously I don't know your requirements but for building features I would strongly prefer smaller environments with in-memory variants of service dependencies
neuron uses nix+cabal, with ghcide as well as HLS (run
nix-shell --run 'code .'
for a ready dev environment). there is docker.nix that builds docker image by copying over nix-build's binary (weights ~150mb). no need for stack or docker, really. use nixos for multi service deploy.