Nix as OS X Package Manager
ariya.ofilabs.com
ariya.ofilabs.com
Then someone handed me a docker image saying, "I got this working on my laptop, deploy this. Isn't this great!". I had no idea how they arrived at that configuration. If something happened to them or that image, I wouldn't have known how to reproduce that "working state".
So I am happy to see Nix / Guix become popular. It is what I imagined package management to be.
Before that we just used rpm and deb packages. For all the hate they get, they are actually well thought out, tested, stable format. With {pre/post}-{install/uninstall} script support, dependency resolution and so on.
But Nix / Guix is a qualitatively different things. Hope it becomes popular.
None of which is to detract from Nix. Nix+Docker is a powerful combination. It takes Docker's declarative nature all the way down to the compiler used to build the bits placed in the container.
Are you screwed if you lose a working .git project, or its maintainer? Well, yeah. But we don't actively worry about that happening all that much because we routinely take precautions like distributing it across developer's machines and servers.
Maybe a commit does break, but that's why we didn't just overwrite the previous image.
But also:
> I only know of a single time when it's valuable to
> "commit" a new image (recovering logs from a stopped
> container)
Not everything's open source.>Not everything's open source.
I don't know what that means. Whether the source is closed or not has zero impact on the use-cases I'm discussing for Docker. Even if the source of the base layer isn't available, it doesn't stop you from using that base layer in a new Dockerfile, at all.
Finally someone understands what really annoys me when I say that docker isn't 100% reproducible if you aren't using version pinning or something similar.
If you have 2 developers, and one of them does a build the next day, you could have them with two different versions of a package when the version went up.
That should scare people.
NixOS on the other hand, builds everything in sandboxes that only expose the requested dependencies. Builds are almost 100% deterministic. The cherry on top is that your system installation is simply a package consisting of all other packages and configuration symlinked together, and you replace your entire system in one go (atomically, by writing a symlink).
If the build fails at any point along the chain, you get an error and your system remains unchanged. You can totally switch from one major release to the next (and back) without hassle.
I worked at a place where they'd spin up a new gentoo box for a dev with stock "emerge foo" and let everything run up.
Then 3 weeks later, they'd do it again for the next dev, and they'd have many diff versions.
I know they weren't "doing it right", but they were doing it the default way they learned, and it caused a lot of problems.
Thanks for the info/clarification though.
Ideally, I want _both_ of them: tagged Docker images being sent between environments (with `docker run -e SOME_ENV=testing imagename` for changing the internal config), but reproducible builds in my Dockerfile. I wonder if Nix can be used to achieve that last part?
> DISCLAIMER: This project is no longer actively maintained and probably broken
and it is actually rather cool since you don't rely on any docker command :)
Images are nothing but a caching technique for the results of deterministic builds.
If you're using them for any other reason, you have a flawed process that is going to come back to bite you some day.
It's a hard problem to solve, and Nix helps a lot, but it's not going to fix all the problems, which is why we should care more about trusting the packager to package something relatively reproducible and sign it so that we it can be vouched for.
Copying a comment I left on that blog post:
---
Q: "What does building a a Docker image using Nix give you over creating a regular Dockerfile to build a Docker image?"
A:
* Better abstraction (e.g. the example of a function that produces docker images)
* The Hydra build/CI server obviates the need for paying for (or administering a self hosted) docker registry, and avoids the imperative push and pull model. Because a docker image is just another Nix package, you get distributed building, caching and signing for free.
* Because Nix caches intermediate packages builds, building a Docker image via Nix will likely be faster than letting Docker do it.
* Determinism. With Docker, you're not guaranteed that you'll build the same image across two machines (imagine the state of package repositories changing -- it's trivial to find different versions of packages across two builds of the same Dockerfile). With Nix, you're guaranteed that you have the same determinism that any other Nix package has (e.g. everything builds in a chroot without network access (unless you provide a hash of the result, for e.g. tarball downloads))
---
IMO, NixOS almost subsumes Docker. Where Docker gives you a way to guarantee that the contents of an image will be the same across two machines, it does little to ensure that building that image from a given Dockerfile is a deterministic. One of Docker's neat tricks is using layers to try to save on disk util -- but even then, Nix beats Docker; if you start two different containers using Nix packages, the common packages are shared across both containers, whereas two different Docker images with an uncommon base would not share the common files.
Docker mostly looks like someone set out to answer two questions:
1. How can we work around all of the problems inherent in conventional package management so we can have a smidgen of determinism?
2. How can we have a relatively nice/cohesive UI around launching/inspecting containers and managing networking/bind-mounts?
#1 can be side-stepped entirely with a package manager that doesn't cause all of those problems in the first place (Nix/Guix).
#2 is still quite useful, but would have been better served as layering on top of something like Nix or Guix (not that I really approve of the "wannabe systemd" daemon approach that Docker employs (all while being, ironically, rather difficult to use with systemd: https://lwn.net/Articles/676831/ )).
Edit: grammar
So what?
You don't need to reproduce that working state: The work has already been done!
Think about how the interactive approach Lisp and Smalltalk programmers use is superior to the compile/run/rerun approach used by C++ and Java programmers -- by exploring the problem in a nonlinear way, you can find your way to a solution more quickly.
Docker package management is like that: You make some changes, you commit often, fork your instance and try lots of different things. Eventually you find something that works and you tag it. You share your tags with other people. This is an extremely productive way to get work done.
If we design systems out of a collection of processes, that we can replace at will, then what we actually have is a collection of checkpoints and branches that we can try simultaneously. Replacing individual branches allows us to try out changes there, without having to rebuild (and wait) the entire system.
Another way to think about this, is that Clojure is more like subversion than like git: It might be better than RCS, but you're still stuck with a linear revision history, and new changes can't completely conflict with old objects and old changes that are created.
And yet another way, is to imagine all of those old class definitions that old objects hold onto are actually global variables, because they have global (time) extent, and yet are unreachable, so it's difficult to find out whether your new code is being affected by those old settings.
Smalltalk, and even other (esp. Commercial) Lisps don't have this problem. It has nothing to do with interactivity, but with hidden state.
It's also a surprisingly convenient way to build tests.
My background is physics, and the philosophy there is "If you can't reproduce it, it doesn't exist."
In my experience with software, sharing opaque binary blobs is a Bad Thing. It doesn't matter if the blob is a proprietary firmware image, or a "open source" docker image. It's a magic unreproducible image. And (to me, at least) therefore suspect.
> Docker package management is like that: You make some changes, you commit often, fork your instance and try lots of different things. Eventually you find something that works and you tag it.
Again, the physics philosophy is "If you don't know what you did, you didn't do it".
For software, programming by randomly trying things is one of the worst methods available. You should understand what you're doing, and understand the system you're building.
> You share your tags with other people. This is an extremely productive way to get work done.
Sure. It's a way to quickly create, test, roll back, and share opaque binary blobs. It's "productive" in that you get things done. But you have no real idea what you got done. You just have a magic image that "works".
I would not hire a programmer with that kind of attitude. I've been burnt by that method, and those kind of programmers, too many times.
I believe this assumption needs to be challenged.
Aside from merely believing that.
I can't even tell what you mean. If I want to rebuild my machine same as before, or deploy build 100s of servers at different points in time with the same setup, reproducibility is necessary.
There's not even one argument against that. If you have any, please oblige us.
Should deployments be a surprise?
No it isn't.
Reproducible is talking about the process or steps needed to make a machine. You do not need to repeat the steps to produce the first machine to produce the second machine. You can simply clone the first machine, and Docker makes this very easy to do.
Keats begins his article with the statement offered, without justification:
In this post I'm going to assume that reproducibility is good and necessary.
Since I don't believe that, and in fact believe the opposite, I take issue with the entire article. Indeed, he goes on to construct a straw man comparing the exact opposite of what I propose -- a non-interactive "dockerfile" and not even a particularly good one, with a very well thought-out "purely functional" build system.
> Any significant contribution you might have for software engineering in regards to that?
I don't know of any big technology company that doesn't design their live infrastructure around ephemeral machines cloned from a template.
They don't all use Docker, and they don't all use interactive development, however, but these two tools can make it easier for people who (for example) don't have Google's economy of scale.
> Should deployments be a surprise?
No they should not.
Deployments take less time with my interactive method because you do not have to wait to "rebuild everything" which can take hours.
Remember heartbleed? People using nixos had to rebuild every component that used OpenSSL, then test them, then find something else broke and repeat the process. Only once they had finished building their new process could they roll out this process to all machines, doing one more final rebuild. Even if done perfectly this has a minimum of two iterations.
I could simply create a new clone, get it working, then tag it as the next release. New instances can then be cloned from the release, monitored, and the old instances destroyed. The entire process took under an hour for the entire fleet, including reading about heartbleed on HN. This is clearly almost as fast as running `apt-get dist-upgrade` on each of the live systems, but without any of the risk.
The organisational complexity, however is admittedly much higher than "wash, wait, repeat", which is why most system administrators could not even begin to use live clones until virtualization and tooling had become much more commonplace.
> I can't even tell what you mean.
And yet you feel qualified to tell me I'm wrong.
Cloning is not the answer since it's all or nothing -- where reproducibility can be achieved piecemeal.
Only offering cloning means that starting with the same initial Dockerfile and making some needed updates to it, one can't be sure that the NON updated parts will be producible. So you don't get flexible reuse.
Only offering cloning also means that when you need to update only a part (e.g. because of security reasons) you can't just use the same dockerfile with the changed component and be sure that you'll get an otherwise identical system. So you don't get flexible updating.
Cloning also required an already set-in-stone configured system to be the prototype.
But until one reaches that, they have to experiment with various configurations (to end with the specific Docker image they want). Without reproducibility, they can't be guaranteed that their various test configurations under test are the same and only their latest changes differ. Perhaps another thing they left as is broke too.
>I don't know of any big technology company that doesn't design their live infrastructure around ephemeral machines cloned from a template.
That's just an argument from popularity. And not necessarily a popularity they opted for, while also being provided with the alternative.
That they use "ephemeral machines cloned from a template" is just what they have to do -- not what would be ideal.
Besides, reproducibility doesn't preclude cloning as part of the process -- it's a superset of it, offering way more flexibility.
>I could simply create a new clone, get it working, then tag it as the next release.
But if you mess with the clone it's not a clone anymore. And if a whole team has to mess with the clone over some period of time to update or fix it, there's hell to keep track what went on.
Nobody argues that you can't simple install cloned images as they are.
What exactly do you mean by flexible reuse, and why is it good? What exactly do you mean by NON updated parts and what does it mean that they are "producible"?
I don't understand your words. Speak plainly!
I don't use a Dockerfile, and the interactive development approach means normally you start up your clone, work on it for a bit, then commit the clone.
Here's an example:
$ docker run -t -i debian:jessie bash
root@264b51d62d0e:/# apt-get install nvi
E: Unable to locate package nvi
root@264b51d62d0e:/# apt-get update
root@264b51d62d0e:/# apt-get install nvi
$ v=`docker commit 264b51d62d0e`
$ docker tag $v whatever
The mistake with the first line didn't mean I needed to start over. In a realistic example that could save me an hour of rebuilding. I can push my tag to other developers I'm working with, and they can use `docker diff` to find out what the differences are.> But until one reaches that, they have to experiment with various configurations (to end with the specific Docker image they want). Without reproducibility, they can't be guaranteed that their various test configurations under test are the same and only their latest changes differ. Perhaps another thing they left as is broke too.
This is a danger that requires discipline. Committing instances is very cheap, so it is good practice to commit often, and keep notes (commit messages). I think version control is common enough that most sysadmins know how to do this, and other packaging systems suffer from the exact same problem.
However because it's interactive, that need to experiment is satisfied fully and the sysadmin/user gets to use all of their tools to develop the working prototypes.
> But if you mess with the clone it's not a clone anymore. And if a whole team has to mess with the clone over some period of time to update or fix it, there's hell to keep track what went on.
This is not true.
With Docker you are encouraged to make many clones and branches and try different things out. The clones that are useful get tagged and forwarded to others.
A sysadmin who isn't using version control has other problems!
> Only offering cloning also means that when you need to update only a part (e.g. because of security reasons) you can't just use the same dockerfile with the changed component and be sure that you'll get an otherwise identical system. So you don't get flexible updating.
I don't understand this complaint, but it mentions dockerfiles again. Let me be clear: I never use dockerfiles.
If I want to update for security reasons (as I mentioned in my heartbleed example), then I will find it easier and faster to implement the fixes interactively. Having to write my recipe for nix-os and run it and wait while it runs is insecure because you are vulnerable longer.
> Besides, reproducibility doesn't preclude cloning as part of the process -- it's a superset of it, offering way more flexibility.
The non-interactive (and "reproducible") approach is slower, less secure, more prone to errors. I don't believe it can possibly be "more flexible" since the interactive approach can do everything the non-interactive approach can do and faster.
I do admit that Docker's build artefacts use more disk space and more Internet bandwidth than plain text nix configuration artefacts, but this is not fundamental to interactive development, which is specifically what I'm advocating.
> Any significant contribution you might have for software engineering in regards to that?
> That's just an argument from popularity. And not necessarily a popularity they opted for, while also being provided with the alternative.
Arguing that "nobody does it so it must be wrong" isn't any better than "just because everyone does it doesn't make it right".
I really think you need to learn about something and make up your own mind, instead of repeating "Docker is Bad" because you read something like that on a blog.
I also think you should really try to understand what I am suggesting before you go arguing against it.
> What exactly do you mean by flexible reuse, and why is it good?
I already explained. What exactly don't you understand?
It's about being able to reuse a recipe (dockerfile, etc.) for creating your final artifact while ALSO being able to change it -- as opposed to the "take it or leave it" case with cloning some binary blob.
> What exactly do you mean by NON updated parts
I wrote "Only offering cloning means that starting with the same initial Dockerfile and making some needed updates to it, one can't be sure that the NON updated parts will be (re)producible".
My typo aside, the meaning is clear: with cloning available but no reproducibility, when you want to only change part of a container (e.g. update a specific piece of software there), you don't know (when you re-create the container) that the other parts you didn't change are as they were before.
Basically that's the very definition of non-reproducibility.
> I don't use a Dockerfile, and the interactive development approach means normally you start up your clone, work on it for a bit, then commit the clone.
That's neither a good, nor a new process. That's like 90's sysadmin work. For one, you're doing all this work manually. Second, if you want to revert/change something you made, you have to mess with your clone, potentially putting it in some weirdo state -- you basically remove the scriptability part and just mess with your clone manually. Or you write some custom scripts or use some provisioning software -- which brings you right back to the reproducibility discussion.
With reproducibility you wouldn't have to commit the clone -- just the "recipe" to make. And you would still COULD commit the clone if you wanted (as I said, reproducibility is a superset of merely working with clones).
> The mistake with the first line didn't mean I needed to start over. In a realistic example that could save me an hour of rebuilding.
Doesn't save you anything over reproducibility -- since the latter does not prevent cloning. But having only cloning (which is what the parent lamented) does deprive you of the benefits of reproducibility.
"In a realistic example", for example, things could get much more hairy (and many more tests and changes could be needed), beyond merely forgetting to update apt before installing nvi.
And all of these changes and process would be totally opaque to your clonable blob.
> I can push my tag to other developers I'm working with, and they can use `docker diff` to find out what the differences are.
The can use docker diff and try to guess what the differences are -- and what the intention was, etc. Because docker diff is just a record of file changes, not of procedures followed and the intentions behind them.
> This is a danger that requires discipline.
And that's (part) of the whole problem. Anything that requires discipline on the human part and that could be automated is busy-work -- and error prone.
> With Docker you are encouraged to make many clones and branches and try different things out.
Yes, but that's meant for safe-keeping, like vm-snapshots. It doesn't replacing actually having a written record of what you did, what's supposed to be istalled inside, and why.
> I don't understand this complaint, but it mentions dockerfiles again. Let me be clear: I never use dockerfiles.
So again, a throwback to the manual devops age.
> Arguing that "nobody does it so it must be wrong" isn't any better than "just because everyone does it doesn't make it right".
Perhaps. But I fail to see where I did that.
> repeating "Docker is Bad"
Who said Docker is bad? Docker is great. It's non reproducibility that's the issue.
You must be confused. I can create and delete files on my filesystem -- changing it -- even though it is a binary blob.
Perhaps you mean something else?
> with cloning available but no reproducibility, when you want to only change part of a container (e.g. update a specific piece of software there), you don't know (when you re-create the container) that the other parts you didn't change are as they were before
I keep hearing you say when I want to re-create the container, but I don't ever hear you say why I would want to re-create the container.
> For one, you're doing all this work manually
Wrong. It's less manual work.
Writing a nixfile is manual work. It's hard manual work because none of the tools are interactive, and it's very distracting waiting for the computer to reply the nixfile between interations.
Interactive development is superior.
> if you want to revert/change something you made, you have to mess with your clone
No I don't. I discard it.
Do you use git rebase on your own history? Or do you create a new branch with a cleaned up history?
I do the latter.
> potentially putting it in some weirdo state
I don't need to reinstall my operating system every day because I get confused about what is on my computer.
> The can use docker diff and try to guess what the differences are -- and what the intention was, etc. Because docker diff is just a record of file changes, not of procedures followed and the intentions behind them.
The intentions are recorded in the commit log. That's why docker commit allows commit messages.
Procedures are only recorded if someone records them. Why would I do that if I only have to do it once?
> "In a realistic example", for example, things could get much more hairy (and many more tests and changes could be needed), beyond merely forgetting to update apt before installing nvi.
That's what I said, but you're missing the part where I don't have to wait hours and hours and hours while nix rebuilds my system over and over again.
"Reproducibility" has enormous costs, and the value proffered can be had with better tools.
> Anything that requires discipline on the human part and that could be automated is busy-work -- and error prone.
You're not automating the nixscript-writing, so you haven't saved any work. You've created busy-work by reinstalling your operating system over and over again.
> Yes, but that's meant for safe-keeping, like vm-snapshots. It doesn't replacing actually having a written record of what you did, what's supposed to be istalled inside, and why.
No, it's exactly a written record because that's what the commit messages are for.
If I want to know what's inside, there are other tools (dpkg) for taking inventory.
> So again, a throwback to the manual devops age.
Nonsense.
For some reason you only want to count the time that your script is running from when it is finished, instead of the time and expertise needed to develop the script in the first place, that you only need to run once.
If you can't understand that a sysadmin's job, whether they are writing a nixfile or they are directly interacting with a machine, is manual work, then I just can't imagine how I can be understood by you.
That's insane.
That's nice for cloning, it's not reproducability.
How do you know that the docker image was created with the right set of software? How do you know that there wasn't anything else added to it?
Copying the final image means you're just copying an opaque binary blob. You can't do the work yourself to build an image, and get the same docker image.
Something like "nix" makes that simple. Everyone can agree on the steps required to get from A to Z. Everyone can independently agree that each step looks the same for everyone.
For me, it's not about ease of use. It's about security and trust.
Define "the right set". If I ask someone to prepare something I might use
docker export/diff
in part of the review process.> How do you know that there wasn't anything else added to it?
docker diff
> Copying the final image means you're just copying an opaque binary blob. You can't do the work yourself to build an image, and get the same docker image.So what?
Why exactly do you think "copying an opaque binary blob" is bad?
Don't you ever copy files on your hard disk?
Have you ever used `dd` to copy a filesystem as a binary blob to a disk or a USB key?
Did you know "git" copies "opaque binary blobs" around? Do you really prefer RCS because the version control is stored in plain text files?
What exactly is your complaint here?
My position is simple, but apparently radical: I recommend interactive development of systems because it is better, and I define better as faster and more secure. "Reproducible" simply is not a goal.
> For me, it's not about ease of use. It's about security and trust.
You should rethink your position.
Nix packages from sources you don't trust aren't more secure than docker images (even if you use nix.useChroot=true), which, barring bugs in docker and Linux, at least get virtual memory, disk, and networking.
Nix packages built by yourself are harder to write than docker tags you build yourself.
Or to get out of computers, you have a drug where they say it cures cancer 100% of the time and no one can reproduce the results, it's fine for you?
Reproducible in this case is talking about the process or steps needed to make a machine. You do not need to repeat the steps to produce the first machine to produce the second machine. You can simply clone the first machine, and Docker makes this very easy to do.
That I can clone a machine does not mean that I can clone the interaction between a patient and their drugs, but if I could it would most certainly be preferable to trying to repeat the experiments with larger and larger populations.
I can't reproduce me, so do I not exist?
And yet you don't know why.
This is called dogma, and dogma is bad: It leads people to make stupid decisions over and over, all the while repeating that the alternative is worse.
If you put your hand in a fire 5 times and it burns you 5 times, you don't have to understand anything about fire to know that you should probably stop putting your hand in. And if you saw other people about to put their hand in, you would mention to them that everytime you put your hand in, you end up regretting it.
We don't need religion to keep from getting burned when we have tools.
Then you don't know what the word "dogma" means.
What I said was "In my experience." i.e. practice learned over time.
This cannot be interpreted by any reasonable person as "blobs are bad, just 'cause."
That kind of straw man argument leads me to conclude that similar issues are being your inability to understand the use of reproducability.
You aren't offering any evidence or justification besides the statement. I might as well say, "in my experience, ``reproducability (sic) is a waste of time''" -- what's the point of that?
Instead, I'm providing concrete examples of why it's a waste of time, and rather than anyone arguing the contrary, they're doing exactly what you're doing: Repeating that there's some point of "reproducability" (sic). There isn't, you're wrong. End of.
But whatever, my background isn't physics, so what could I possibly know? Running engineering teams for the last two decades that have to develop and deal with high volume, high uptime application servers only means my systems have to work and make money, not that they be "reproducable" (sic).
I mean, it was a win, not having to untangle the hairball of rpm/deb dependencies. I like that part too. But not understanding the downside was what shocked me. It was like everyone drank the magic kool-aid potion.
Really? This surprises you?
The gross majority of the world runs software this way.
The real issues are quality control and release process.
That's exactly how I recommend applying patches.
If it breaks things you can fix it.
You don't do this on a live system: If you're smart and your live systems are ephemeral you can copy your image, get it working, verify it interactively, and once you have a working image, you spin up new instances using the new system, and start shutting down the old systems.
We've been down this road before. Hacking something until it works, then imaging it and passing it on to your coworkers is a maintenance nightmare.
For example, did you read that article?
You know Docker has none of those problems
> The first problem with a snowflake server is that it's difficult to reproduce. Should your hardware start having problems, this means that it's difficult to fire up another server to support the same functions.
docker tag/push/pull
> If you need to run a cluster, you get difficulties keeping all of the instances of the cluster in sync. docker start tag
> You can't easily mirror your production environment for testing. docker commit/export/import
> When you get production faults, you can't investigate them by reproducing the transaction execution in a development environment. docker push/pull
> The true fragility of snowflakes, however, comes when you need to change them. Snowflakes soon become hard to understand and modify. Upgrades of one bit software cause unpredictable knock-on effects. docker history/diff
The issue that Martin is describing is one where people work on the server - update it in place, and do not have any change management process besides "do stuff" and any quality checking process besides "check stuff".He spent over 500 words to say that's probably not a good idea.
No kidding.
Now.
What I'm actually advocating is interactive development: The exact same way we develop our APL workspaces and our Smalltalk images, can be used to design and build server infrastructure.
Instead of a development cycle that goes:
* Edit nixfile/dockerfile/vagrantfile
* Wait (sometimes hours)
* Test things
* Repeat
You get a very short:
* Do stuff
* Get happy
* Tag the results
Then when you want to run "in production", you publish your tags/image, review the history with your team, not dissimilar to how Smalltalk people will clean up their changes file, and then introduce the new version into the cluster. Once you're satisfied the world hasn't ended, you can decommission the old images.
You can even make scripts that do these many of these steps automatically (divert x% of traffic to new instances, collect results, and so on). If you have a well-designed infrastructure, you can have junior developers use those scripts to make changes on their first day.
Now.
I get it, you have realised that if you start over often enough you get good at it.
I get that you've seen people who treat systems development as a completely linear descent into unmaintainable bat barf that can only be recovered by a bad day reinstalling everything and "starting over",
I also get that you think amortising that cost by taking those hours to rebuild your entire system when glibc changes is necessary to avoid that bad day.
I also get you like static external configuration like Dockerfiles or Vagrantfiles, and view them as instrumental in the audit of that process.
However while you understood that I disagree with that, you didn't understand why: If you had a decade or so of Smalltalk or Lisp experience instead of just ruby experience, you might have known exactly what I meant, but instead of asking, you just ignored the part you didn't understand, making a straw man argument that you did understand.
After all, what's more likely, someone knows something important that you don't? Or someone knows something unimportant and just isn't as smart as you?
The "build from source" camp provides solutions to a couple problems that at first glance seem to be difficult in a "principled mutation of binary image" model:
- Moving to a different architecture, e.g. x86 to ARM or ppcle. In build from source, I essentially change some parameters and components in the lowest level and rebuild.
- Other big changes to base components, e.g. compiling for Linux/libc vs. building atop a unikernel (obviously heavily dependent on what the upper layers are doing, but I can imagine wanting to run similar code atop multiple bases).
- Sharing common components. This is not really a "from source" thing, but is an important part of Nix. If two images include components in different orders, in Nix the common components will be shared (at the arguably coarse granularity of a Nix package), while the linear structure of Docker layers means once two images diverge, everything further down the chain will be its own copy, even if the same bits.
However there's another, perhaps simpler solution: I can have a separate "wu-ftpd" container, and a separate "hylafax" container. This admittedly might not have been as easy when nix started, but it is very very easy now.
From what I can see, there are two philosophically pure approaches to software packaging. Either we can build everything from source, so that we have reproducibility (see e.g. emerge for a practical system, although it is still not bit-for-bit reproducible) or else we can ship everything as a blob so we have total consistency (e.g. Docker, Go).
The other approaches, apt and nix, seem to me to be compromise positions. They may even be practical or useful compromises. But they are ultimately about the degree to which we allow binary blobs, not whether we do.
But it seems to me that once you have accepted installing binaries via apt then installing them from Docker is a question of degree, not of kind. It seems very hard to me to argue that distributing binaries via Docker is really worse than via nix.
>But they are ultimately about the degree to which we allow binary blobs, not whether we do.
I don't agree with this assesment of Nix and Nix packages at all. My local system evaluates the package definition (a description of how to build it from source) and merely is smart enough to retrieve a prebuilt binary with the exact same source inputs and configuration. Sure, I'm still trusting the remote party to have built it as described, but that's true of any system where pre-built binaries are distributed.
For another explanation, I could disable the NixOS Hydra Cache and could theoretically rebuild my current system state without their binary cache and still wind up with the exact same bits. At least, that's the idea behind Nix.
Nix is really both of these combined, and is not like apt/dpkg in how it separates source and binary packages.
A Nix package is really instructions (including all compiler flags) of how to build something from source [1] and nothing more. Due to being pure functions, given the binary inputs (e.g. a zlib dependency previously built) and the instructions, any two people will always get the same binary output upon executing those instructions [2].
Furthermore, without executing the instructions, I can generate the unique identifier of the resulting binary package by hashing the inputs and the instructions. This unique identifier can be used to determine if the package already exists locally in /nix/store, and optionally be used to check if some trusted binary cache (such as Nix's binary cache) already has the binary package.
There is no binary package in the sense that apt/dpkg have source packages and many derived binary packages which are installed without need of the source package.
For example (contrived and simplified):
gcc-uniqueGccHash = bootstrappingProblem()
libc-uniqueLibcHash = buildLibc(gcc-uniqueGccHash)
zlib-uniqueZlibHash = buildZlib(gcc..., libc...)
myprog-uniqueMyProgHash = buildMyProg(gcc..., libc..., zlib...)
To download "myprog" from a binary cache, I can't calculate uniqueMyProgHash using any old versions of libc and zlib; I must have exactly the same hashes that were used by the system that built "myprog", all the way up the dependency chain. And I'll only ever have that if the builds are reproducible and/or I downloaded them all from the same binary cache.[1] In practice, there's nothing stopping the instructions from saying "download this binary blob" instead of "download this source tarball".
[2] The problem of build reproduciblity is not solved, but many outside of Nix also see this as important, see https://wiki.debian.org/ReproducibleBuilds
Is a Telegram bot that queries the RAE dictionary (Spanish). It doesn't have any documentation yet, though :S
You can look for the project deps code here: https://git.hso.rocks/hso
Do you write default.nix files by hand, or there are some tools to automate the process?
I suppose for a project with a lot of dependencies (one of my current requirements.txt is over 100 SLOC long - almost all deps are on PyPI; haven't checked nixpkgs as I'm stuck with Debian at the moment) it would be somewhat tiresome to check every package and for every missing one list all the dependencies.
The nixos.org website is a bit sparse on the Nix project's history, and same with wikipedia (though separately mentions that NixOS was a research project started by Eelco Dolstra in 2003).
http://nixos.org/~eelco/pubs/phd-thesis.pdf
He also wrote this InfoQ article on the subject of Nix and NixOS which may have some useful information (2014):
https://www.infoq.com/articles/configuration-management-with...
The Wikipedia pages for Nix and NixOS also have some interesting references.
https://en.wikipedia.org/wiki/NixOS
https://en.wikipedia.org/wiki/Nix_package_manager
Edit: I have been playing with NixOS at home for a few months and I think it's very interesting, but I'm a bit surprised I haven't heard more about it over the past decade or so. I'm thinking that the recent industry focus on deployments and provisioning has caused more people to come across it.
* 2007 NixOS Linux distribution
* 2009 Hydra Continuous Integration
* 2011 NixOps Cloud deployer
* 2013 First stable NixOS branch
(from http://wmertens.github.io/nixos-cfgmgmtcamp-slides/#/1/1 - non-authorative source :))
- There is a version history in the release notes appendix of the manual. [0]
- As others have posted here, the project is currently hosted on github [1], though it was originally in a svn repo. [0]
- Eelco Dolstra is the active project maintainer. [2]
- First checkin for Nix was ~March 12, 2003 (in the file nix.c/nix.cc). [1]
- Version 1.0 release on May 11, 2012 (also first release from github). [0]
[0] Release Notes: http://nixos.org/nix/manual/#sec-relnotes
[1] https://github.com/NixOS/nix
[2] https://github.com/edolstra
Edit: Thanks to sibling posts for the other info they were able to dig up for us.
I'm looking forward to future posts that show how Nix is better than Homebrew or MacPorts, because that hasn't been demonstrated so far.
And as a bikeshed nitpick, I was always unhappy with the meaningless rpm options, preferring apt's clearer options (rpm -qa vs aptitude search). I'm a bit disappointed that nix chose to mimic rpm's style. Oh well, tomayto/tomahto.
apt-cache search
aptitude isn't necessarily installed.The new apt, if you have it, now combines apt-get with apt-cache plus a bunch of UI improvements.
apt search ...
apt install ...
If you've got it, it's well worth a look. $ pkgin se python3
python35-3.5.1nb2 Interpreted, interactive, object-oriented programming language
python34-3.4.4 Interpreted, interactive, object-oriented programming language
python33-3.3.6nb3 Interpreted, interactive, object-oriented programming language
along with a bunch of pre-packaged modules: $ pkgin avail | grep ^py3 | wc -l
1412
Let me know if anything you need is missing and I'll add it.I have just found a related issue: https://github.com/joyent/pkgsrc/issues/331 Unfortunately, from what I read there, I have still to use homebrew or install from python.org (with those I have no issue). I really enjoyed pkgin otherwise.
Homebrew is downright dangerous (I've written about why: http://inthebox.webmin.com/homebrew-package-installation-for... ).
MacPorts is fine. Not wonderful, but OK. Not reproducible in the way Nix is.
PkgSrc is also pretty good. Also not reproducible the way Nix is. But, the packaging policies are good, the packages tend to be well-thought out, and the technical solution to stuff like dependency resolution is pretty OK. The people building and maintaining PkgSrc know what they're doing.
Nix is clearly superior to all of them, however, in being reproducible, easy to roll-back in a predictable way (try going backward with almost any other package manager, even RPM/yum and dpkg/apt can't do that...I love'em both, but recognize that we now have better solutions to package management). Nix is just a fundamentally better tool.
Flatpak is another really promising packaging technology with some of the same benefits (but it's tackling things in a different way; I think I still have to consider Nix a better overall solution; it is certainly simpler in both implementation and usage, for packagers/developers and end users).
But, don't use Homebrew. For now, PkgSrc is probably the best method of getting a lot of reasonably good packages for Mac OS X. Nix will be the better option eventually, once there's a lot of people packaging for it (assuming people get over their bizarre love affair with Homebrew and start working with Nix, instead).
Your link specifically covers the case of Homebrew on the server. But Homebrew isn't meant to be a server package manager. It's an OS X package manager, and OS X is a desktop operating system, not something that most people choose to use as a server (OS X Server exists but is primarily used for intranet stuff rather than as a production server). The complaints that your link has are only problems for an external-facing server. They're not problems for desktop use. And the simple answer to that is: don't use a desktop package manager on your server.
The wording dangerous was poorly used, inappropriate for server package management would have been better.
So, yes, to be clear: The dangers of Homebrew are primarily when considering it for use in a server context. I personally wouldn't use it anywhere. But, it has many good qualities that many people value highly.
If you want to say "Homebrew is dangerous when used on a public-facing server", that's entirely fair. But that's not what you said. I'm glad that you do recognize this distinction though, and I hope you will think twice before claiming again that Homebrew is dangerous without qualification.
Install emacs with brew Install spacemacs on top of emacs It works!
To pull users from brew it needs to work at least as well, especially for common tools like emacs. Go check out the number of issues related just to emacs installs, it is kind of amazing.
Just about everything else I'd like to use is packaged, so Id like to think we're over the adoption-pkg-count chicken egg problem.
Using nixops to push a closure from your local system up to ec2 is very pleasing. It feels like the future.
For a toy project this is not a problem of course. But before I'd use nixos in production, it needs a better story for security patches. If you try to look up the word 'security' you get viagra spam on their wiki. and there is no security mailinglist whatsoever iirc.
I'm sure this will improve over time though. And for development, nix is still a saviour.
Rather than rebuild everything that depends on the fixed glibc, we build the fixed glibc, then rewrite all references to the old glibc to refer to the new one. That rewriting happens in new copies of the referring packages, of course, since the "store" is immutable.
https://www.gnu.org/software/guix/manual/html_node/Security-... https://savannah.gnu.org/forum/forum.php?forum_id=8470
Luckily guix is a better UX for nix. =)
I see that there's a bunch of `nix-whatever` tools around, but even their names aren't much sensible.
I'm not in doubt that nix is technically superior to homebrew, but UX-wise it's at least been a disappointing experience for me.
A big help is IMO to get a solid understanding about how nix itself works[1]. Also check out some existing `configuration.nix` files on GitHub. Just google for "github configuration.nix". This might give you some inspiration what you can do with the `configuration.nix`.
If you have some trouble with customizing vim on NixOS/via nix, checkout this[2] blog post I wrote a month ago. Hope it helps :)
[0] http://blog.tinco.nl/2016/02/05/nixos-on-digital-ocean.html [1] https://nixos.org/nix/about.html [2] https://www.mpscholten.de/nixos/2016/04/11/setting-up-vim-on...
http://zef.me/blog/5981/deploying-a-simple-node-js-applicati...
Step 1: I have to download a gig of stuff before I can install any packages? Even though my drive is big nowadays, that still seems like a bit of a waste...
Step 2: Let's try building a package, the last thing I installed with homebrew.
> cd math/minisat
> make install clean
Makefile:27: *** missing separator. Stop.
> make
Makefile:27: *** missing separator. Stop.
Hmm.. this isn't the most user-friendly system...UPDATE: I have to use the bmake I bootstrapped, didn't realise (and the quick-start docs didn't mention it). Now building begins, but the executable doesn't link.
[1]: https://robots.thoughtbot.com/brewfile-a-gemfile-but-for-hom...
How does it handle OS-specific dependencies/distinctions? Or if e.g. a package requires ruby or python to be installed?
With Nix you just declare postgresql as your package inputs and you are done. All needed parts to build and install psycopg2 are there.
Your package/project is completely isolated from your OS libs.
From my few days of trying to replace Homebrew with Nix, being able to rollback the environment to last known good state (`nix-env --rollback`), ability to keep track of package changes (`nix-env --list-generations`), ability to try a package without installing it (`nix-shell`, well, technically it's installed on the machine, but nothing is linked in the profile, and you can nuke them with `nix-collect-garbage`) are very nice additions.
Apart from these features, the fact that it doesn't chown `/usr/local/` to my user is a big plus. Nix will also not linking anything not explicitly installed by user to their profile (e.g. if package A has B as dependency, B won't be linked until user actually choose to install it, which is then managed separately from B that were explicitly installed as A's dependency).
That said, I've since revert back to Homebrew because there are few packages broken that are crucial to what I'm working on. Creating and fixing package is not too hard (I did with aria2c), although time didn't allow me to do it back then. I would love to try to switch to Nix again sometimes.
People in ##nix-darwin are super-nice, also! :-)
[1]: https://github.com/NixOS/nixpkgs/pull/15029
[2]: https://nixos.org/nixpkgs/manual/#users-guide-to-the-haskell...
I can setup scripts that install and configure things for emacs to make sure it always runs in an environment that has all it needs. But with the benefit of not having to have everything installed globally.
I can have multiple versions of whatever installed into /nix at once. But still not have to worry about breaking things that need both versions.
As for package availability, I've not hit any issues. It has most of what I personally need.
What I hit more often is random breakage or having to update derivations that only ever got created or maintained from a linux perspective.
I'd say its in the "developer not afraid to fix broken build problems" category as a package manager. Its not bad, but it is really annoying when you have to revert back to a prior generation because some package you use got updated and now won't build because of its test suite failing.
Random stuff like that happens a few times a month, nothing huge though honestly.
https://github.com/NixOS/nixpkgs/tree/master/pkgs
...and search here if you're looking for something in particular:
https://github.com/NixOS/nixpkgs/blob/master/pkgs/top-level/...
Stop doing this.
Look, even if you are rolling your eyes and thinking, "it's https and I'm not Ed Snowden, I think I can afford the risk for the benefit of an easy install process", what happens if curl is interrupted? Are you excited at the prospect of a half-ran install script that you didn't even look at?
Though I do agree wholeheartedly that piping scripts to shell, sight unseen, is a remarkably awful idea.
What attack vector would you actually be protecting against if you downloaded a .tar.gz full of binaries and extracted it to /, then started installing things with it?
I understand this path is configurable, but you'll be compiling everything yourself, unable to use Nix's binary cache. You also risk running into unique problems since everyone else is using /nix
I'd recommend everyone try nixos out as well to see why everything (mostly) in /nix is a good idea.
nixos is easily the most un unix unix i've seen, and honestly i can't wait to get rid of apt/rpm/yum/zypper. Not dealing with /some/file/blah being version X or Y in general is so freeing.
Not necessarily. Homebrew compiles stuff for a special prefix then substitutes your own prefix when you install the binary. It doesn’t work with everything (that’s why some compiled packages are available for /usr/local only) but it’s a start.
Homebrew just assumes that the needed executables will be available on $PATH and that the dynamic linker will find libs in /usr/lib or /usr/local/lib.
So yes, you really would need to recompile everything if change the prefix from /nix/store to something else.
Edits: fixed spelling and structure from originally typing via phone.
Kind of a funny situation. If you look at the script first, you'll notice that it would have been okay for the download to be interrupted and so, assuming everything else is kosher with you, the '| sh' would have been "safe".
And if you don't look at the script first, you'd never know that what you just did was "safe."
Schroedinger's-pipe-to-sh.
Because it goes further than just downloading a .tar.gz. It checks your OS and its dependencies; download the right tarball; check it has an install script; run the install script.
If you are installing software from scratch, you must trust the https server that serves it to you.
They could publish sha256 check sums to https://nixos.org/hashes, but you would have to trust the https server.
They could publish their gpg key to https://nixos.org/gpg, but you would have to trust the https server.
Things would be better if we had a reasonable certificate system for verifying open source software, but we don't. There is no gpg web of trust. There is no hierarchical CA system for code signing that open source authors can easily plug into, and expect their users to verify.
If you care this much, stop astroturfing and go build a system that does better. It will be very hard work, and you will have to distribute the first version of your solution with an https server that your users will have to trust.
[1]: Ok, so maybe the instructions could be "curl <url> -o install.sh && sh install.sh".
With HTTPS, you only ever have the choice of trusting the 1000+ CAs in your keyring.
No one has signed it, because the mythical web of trust does not exist. What now?
If this were software that I had to install multiple times (say there are several updates a year), it might make sense to implement TOFU with a published GPG key -- each successive update would use the same key I imported the first time. However, the nix installer installs a package manager which is self updating, and therefore implements TOFU itself.
This is incorrect - you are forced by `curl | sh` to trust all of the 1000+ CAs in your OS' keystore. And if you can't trust all of them (since some have given out Google certs before, you can't), how can you trust what you're getting over HTTPS?
With hashes, they are not typically served from the same domain. That's the minimum level of protection for the user.
Signing with a key is another level of protection; it lets the user (not the OS) decide the level of trust, and even verify the key with the author of the package via a completely separate channel.
Now the question really becomes, why doesn't the provider use the existing (secure) channels of software distribution? RPMs, DEBs, installer packages for Macs, MSAs for Windows?
If you don't trust them, they should not be in your certificate store. That's not a problem with using `curl | sh`, it's a problem with the certificate store and what the user is trusting.
http://ariya.ofilabs.com/2016/05/nix-as-os-x-package-manager... is served over HTTP. Saying "Copy and paste this `curl | sh` command" allows this attack: http://thejh.net/misc/website-terminal-copy-paste
Absolutely nothing. The script is written intelligently, so an interrupted download just results in a syntax error. Specifically, the entire file is wrapped in {}, and if the terminating } is missing, sh will throw an "unexpected end of file" error instead of executing anything.
――――――
¹ — https://derflounder.wordpress.com/2015/10/01/system-integrit...
SIP only prevents users from modifying system level files. It does not prevent you from adding to, or deleting your own files within the `/` directory.
It's only when you try to delete something like /Library, that it then complains.
This is one reason why I strongly believe SIP is blown out of proportion. I've used El Capitan since the betas and created root directories, deleted them, added to existing root directories without having to disable SIP.
Again, it's only when you try to modify the built in directories inside / does it then complain, and frankly, I am more than happy with that, even with sudo privileges.
like i needed another folder in root, as if `/opt/`, `/usr/local/` or `~/.local/share` weren't good enough and this is somehow a feature. another package manager that has less features than homebrew? oh, like fink and macports - no thanks.
With /usr/local/{bin,lib,include}, how would you install multiple versions of openssl? Would you violate convention and do something like /usr/local/openssl-x.x.x/{bin,lib,include}? How do you avoid file path conflicts? When a package is built against a particular version of openssl, how do you ensure that version of openssl sticks around, and that that, upon execution of said program, the dynamic linker loads specifically that version of openssl and not one of the others?
While I encourage you to consider those questions, the short answer is "you can't". At least, not with conventional package managers like Homebrew.
Because Nix builds each package in a unique prefix (governed by the hash of all of the build inputs, recursively), the Nix package manager can use chroots (and Sandboxes on OSX) to ensure that packages are deterministic, and that each package refers specifically to the versions of the dependencies they need (dynamic libraries are fixed via rpaths and we apply patches for references to executables -- so "python -m foo" becomes e.g. "/nix/store/<HASH>-python-2.7/bin/python -m foo").
If you don't use a unique prefix per package, the problem of "how do I install stuff without worrying about path collisions amongst the multiple versions of requisites" becomes intractable. Ditto for build determinism, binary caches, lightweight environments a la nix-shell, etc.
I suppose we could shoehorn each package into /usr/local/<HASH>-<NAME>-<VERSION>/{lib,bin,include}, and then we could tick the "use an existing directory under /" box, but is that really better? I don't see what we gain from that, since that's already an abuse of /usr/local.
I guess my main issue with the original article was that if you don't already know what nix is, the benefit is not at all obvious, especially for the (above?) average joe for whom homebrew works fine. For a sysadmin who is sick of dependencies breaking, even when using something like aptitude, it sounds perfect though.
Although after skimming the nix docs [1] and reading your reply, I'm still not sure why e.g. `/opt/nix/` wouldn't work. Maybe it would, except for the fact that `/nix` was decided on and if you now change that, the prebuilt packages break (see [1])? Obviously, it isn't a deal-breaker, but e.g. consider an existing backup solution which might have been configured inclusive, not exclusive.
Also, I get that as a package manager, nix is in a different position than most programs, but putting stuff in `/` isn't a great precedent, and other package managers don't AFAIK.
For example, installing Datomic via Brew will result in databases being stored in:
/usr/local/Cellar/datomic/0.9.5206/libexec/data
How would that work with Nix? Would the data live under /nix?
And most importantly, would that data get squashed because of some kind of idempotency assumptions that Nix might make?
Yes.
>Would the data live under /nix?
No, Datomic will be built with another default data directory.
>would that data get squashed because of some kind of idempotency assumptions that Nix might make?
No, since data is stored separately.
The reason is because when the software is compiled, it's stashed in the /nix directory, and might refer to other artifacts in /nix. For example, zlib.so exists in /nix/store/zlib-.../lib/zlib.so, and links to glibc.so, which exists in /nix/store/glibc-.../lib/libc.so.6. So if you want to install zlib and get binary packages, you need to first install glibc. And you need to put glibc in the place that zlib will expect it.
But being able to have multiple / separated environments on my machine would be hugely beneficial.
Working for a full-time software consulting agency, I'm normally actively working on many projects at the same time, each of which have their own nuances of packages that are required (e.g. different versions of PHP, different sets of dependencies, etc.) So if nix truly offers seamless switching between environments (and if it can do it quickly and efficiently), it would definitely be worth it for me to look into it further.
> Working for a full-time software consulting agency, I'm normally actively working on many projects at the same time, each of which have their own nuances of packages that are required (e.g. different versions of PHP, different sets of dependencies, etc.)
I don't think this is what Nix offers. This is more like a homebrew competitor (I assume you don't use homebrew to install php...).
See also this comment (great uncle post?) https://news.ycombinator.com/item?id=11773206
It talks about being able to setup and instance of emacs referencing a specific nix environment.
Edit:
This comment also talks about using it to solve virtualenv shortcomings. https://news.ycombinator.com/item?id=11773036
Nix is quite different from just about any other package manager out there. If there's something that seems unfeasible or impossible, please feel free to ask specific questions to challenge my assertions. Nix is so different that it will be tempting to think "surely he means... no, that can't be possible, he must be mistaken... but how would that work?" Rather than silently thinking those things, please feel free to skip right to asking "how?"
(For more details, see also my long post to your original statement/question)
Nope. They both do package management, but that's where the similarities end.
Homebrew packages are not patched to point at precisely the version of the stuff they were built against. For example, if you have program that dynamically links to openssl, that program is built with the expectation that the dynamic linker will be able to find e.g. libssl.dylib in either /usr/lib or /usr/local/lib (the specifics are harrier, but that's a decent approximation). If you later upgrade openssl and the ABI changes, you've now silently broken everything that uses it -- you'll now get a segfault at runtime (I've had this happen with openssl and many other libs on Homebrew, and is part of why I was (and still am) very excited about Nix).
When you compile a package with Homebrew, you're not guaranteed to end up with the same result as someone else, even if they have the same checkout of the formulae. Why? Well, each project's Makefile (or what have you) will try to run whatever's on $PATH, and poke around elsewhere on your system to e.g. automatically enable build flags (oh, luajit is installed? I guess I'll just go ahead and enable Lua scripting and link to it).
How does that compare with Nix?
Nix packages are patched so that their runtime dependencies (dynamic libraries, programs to be execed, shebang lines, etc) are locked down to the precise build that was specified as part of the package. The obvious implication here is that Nix packages can be trivially installed with differing versions of dependencies as necessary (back in the day, I wanted to play with both the Elixir langauge and the Riak DB, but they required two different major versions of Erlang. Unfortunately, I could only install one version, as the packages for both versions wanted to be placed in the exact same prefix and the filenames would overlap. This was on Ubuntu, but it applies equally to Homebrew).
When compiling a Nix package, I can rest assured that the build artifacts will be equivalent between two different machines (not bit-for-bit, but at least functionally equivalent -- excepting compiler bugs). Why? Because the build happens in an isolated environment where, as far as the build process is concerned (we take advantage of a number of kernel features on Linux (chroot) and OSX (Sandboxes)), the system only consists of the packages explicitly listed as build inputs -- nothing else. Excepting esoteric stuff like someone intentionally trying to subvert chroots and such, if you were to put a gun to my head and tell me that you were going to shoot me if you could find a case of non-determism, I'd shrug. We're serious about that whole determinism thing -- it's a core selling point of Nix, and the reason for Nix's "quirks" (the per package prefixes, hashing of build inputs, chroots, lack of network access, etc).
If the above doesn't make the differences obvious, I'll challenge you to answer some questions (the socratic method):
1. How does Homebrew support having two versions of openssl installed concurrently? Describe the paths involved, how the dynamic linker would be guaranteed to load the respective version of libssl, etc.
2. How does Homebrew ensure packages are built exactly the same way across two different machines (e.g. using the same version of autotools, clang, make, etc)? Be sure to explain how the build is guaranteed to not auto detect/enable stuff because of things present on my system that may not be present on your system. Note that Nix will deterministically fail if you fail to explicitly list all of its dependencies (rather than working because, say, cmake just so happened to be installed on the formula author's machine) -- so you'll need to describe how Homebrew achieves this when running a formula.
For example, when building postgresql on OSX, it always links against libpq.dylib in /usr/lib, not against the one it just built. In theory, it could be possible to exclude /usr/lib from the -L path, but then I would lose other libraries, that ship with the system (i.e. libz, libxml2, libxslt, libpam, libSystem). The other possibility is to build all these dependencies too - except libSystem...
Right now I'm using install_name_tool to fix the binary, but that is an ugly hack. (The other problem in this case is, that system libpq is x64-only. It means that i386 build would fail).
I was pleasantly surprised, as I could rollback the entire state of my system from GRUB itself if anything went screwy.
http://chem.tufts.edu/answersinscience/relativityofwrong.htm