[1]repl.it [2]Features [3]Jobs [4]Blog [5]Pricing [6]Jam☀️[7]Sign up[8]←
          Back to blog [9]Edit on Replit☀️
       
          [10]Will Nix Overtake Docker?
          =============================
       
          Mon Nov 29 2021 by Connor Brewster
       
          In many discussions about [11]Nix, the comparison of Nix and [12]Docker
          [13]comes [14]up [15]frequently. This question could be dismissed by
          saying that Nix and Docker are different tools that solve different
          problems. One is a container deployment toolkit and the other is a
          package and configuration manager. However, these tools do have some
          overlap: they can both be used to create reproducible environments. A
          reproducible environment is one that can be recreated from scratch in
          an identical way (ideally bit-for-bit). Practically, this means having
          the same tools, versions, and configuration between the environments.
       
          Reproducible environments are useful to ensure all developers on a
          project have the exact same set of tools. Additionally, you can
          develop in an environment that is similar to the production
          environment -- leading to less surprises when deploying.
       
          Both tools can solve the age-old problem of "it works on my machine".
       
          elon in front of broken cyber truck window with caption "it worked on my machine"
       
          While both tools aim to solve this problem, they take different
          approaches.
       
          Reproducible Environment with Docker
          ------------------------------------
       
          Docker provides tools for creating container images. An image stores
          the contents and configuration for a container. This is usually the
          filesystem, some environment variables, and a run command.
       
          From an image, you can create new containers that will be functionally
          equivalent, even across different machines.
       
          You can distribute images to other developers to give them identical
          environments or you can use them to ship your service to production.
       
          Docker images can be created by a Dockerfile. This file tells docker
          the commands to run to build an image. This can include copying files
          from the host system or installing packages using a package manager
          like apt or apk.
       
          [16]Example:
       
          FROM       node:   12   -alpine   RUN    apk add --no-cache python g++ make      WORKDIR    /app      COPY    . .      RUN    yarn install --production      CMD    [   "node"   ,    "src/index.js"   ]
       
          Docker will create a new container that is used to build the image. A
          new filesystem layer will be created for each command in the
          Dockerfile before the command is executed. Docker utilizes [17]union
          filesystems, which allows stacking multiple filesystems or directories
          to appear as a single filesystem.
       
          Since each command has its own layer, Docker can detect which layers
          are still valid and which ones need rebuilt after changes are made to
          your project. Properly ordering commands will result in much faster
          docker image rebuilds.
       
          Each layer can also be shared across multiple images. This is useful
          when multiple docker images are built from the same base Docker image.
          For example, the above Dockerfile has node:12-alpine as the base
          Docker image. The layers from node:12-alpine only need to exist in one
          place and can be shared by other Docker images.
       
          Once an image is built, it can be named, tagged, and uploaded to an
          [18]image registry so it can easily be shared with others.
       
          While an image allows for reproducible Docker containers, Docker
          doesn't have any guarantees that creating images is reproducible -- If
          you run docker build twice with the same Dockerfile you might get 2
          images that behave in different ways. For example, a third-party
          package could silently be updated and [19]cause [20]breakage.
          Aggressively pinning dependency versions helps, but doesn't completely
          prevent this issue.
       
          Another thing worth noticing is that Docker only allows for composing
          images by inheriting from a single layer. You can specify the base
          image for a new image in the Dockerfile, but you can't take 2 images
          and combine them. If you want a container with node and rustc, you
          can't combine the node and rust images. You would have to start with
          the node image and install rustc manually in the Dockerfile or vice
          versa.
       
          Reproducible Environment with Nix
          ---------------------------------
       
          Nix takes a [21]first-priniciples approach to reproducible builds and
          package management. Nix provides a whole build system that allows for
          building packages in an isolated way.
       
          To build any package, you need to:
       
            1. 
       
              Construct an environment with all the tools needed to build the
              package.
       
            2. 
       
              Execute a build script to run all the build steps.
       
          This process is repeated for every package and Nix goes to great
          lengths to ensure each each environment that packages are built in are
          reproducible. Nix will limit network access, filesystem access, and
          sometimes even run in a sandboxed container during building to prevent
          any sort of external influence during package building.
       
          Packages can depend on other packages which creates a large dependency
          graph that Nix will traverse and build packages on the way.
       
          It just so happens that you can create new reproducible environments
          by adding a new node to this graph. Then you can use Nix to do part 1
          of package building: constructing the environment.
       
          In development, you may not want or need Nix to do step 2. Instead you
          can have Nix drop you into a bash shell where you can run the build
          commands yourself.
       
          Nix comes with a tool called nix-shell which will do exactly this for
          you. Add a shell.nix file to the root of your project and nix-shell
          will pop you into an environment with all the specified input packages
          available.
       
          Example:
       
          { pkgs ?       import    <nixpkgs> {} }:
          
          pkgs.mkShell {
                 nativeBuildInputs    = [ pkgs.rustc pkgs.cargo ];
          }
       
          Nix does not use containers here, it only modifies environment
          variables. For example, binary packages are added to the PATH
          environment variable.
       
          Since Nix provides strong guarantees about reproducibllity, sharing
          this shell.nix is all that's needed1 to give developers functionally
          equivalent development environments.
       
          Unlike Docker, Nix is a full-blown package manager. This makes it
          trivial to compose environments. Taking the example above from Docker,
          if we want node and rust in the same environment, just tell Nix that
          they are both build inputs:
       
          { pkgs ?       import    <nixpkgs> {} }:
          
          pkgs.mkShell {
                 nativeBuildInputs    = [ pkgs.rustc pkgs.cargo pkgs.nodejs-   16   _x ];
          }
       
          Which Should You Use?
          ---------------------
       
          Before considering this question, we should go back to the beginning:
          Docker and Nix are completely different tools.
       
          Docker images are just a sliver of what Docker provides. Docker
          provides tooling for a whole container ecosystem.
       
          Whereas Nix is primarily designed for building packages & environments
          in a reproducible way.
       
          If reproducible development environments are all you are after, Nix
          will serve you well.
       
          If you're looking for a way to build, package, and deploy your own
          service. Docker will provide much more rich tooling. After all,
          containers are the de-facto way that web services are deployed these
          days.
       
          Even so, docker image building leaves a lot to be desired. Luckily, I
          have an ace up my sleeve: Nix can be used to build Docker images.
       
          Example:
       
          { pkgs ?       import    <nixpkgs> { }
          , pkgsLinux ?    import    <nixpkgs> {    system    =    "x86_64-linux"   ; }
          }:
          
          pkgs.dockerTools.buildImage {
               name    =    "cowsay-container"   ;
               config    = {
                 Cmd    = [    "   ${pkgsLinux.cowsay}   /bin/cowsay"       "I'm a container"    ];
            };
          }
       
          Maybe we can have the best of both worlds:
       
            * Reproducible Docker images via Nix
       
            * Simplified deployments via containers
       
          Nix at Replit
       
          If you've been following along, we're currently transitioning from
          providing development tools from a massive Docker image, [22]Polygott,
          to [23]providing packages via Nix. Checkout what users are building
          with [24]Nix on Replit Apps.
       
          We've found that Nix has solved some pain we were experiencing with
          Docker images:
       
            * 
       
              It's very difficult to know what changed between 2 different
              builds of Polygott.
       
                * Un-pinned packages might have been updated and could cause
                  breakage in repls
       
                * Some package repositories may go offline and can break the
                  image build
       
            * 
       
              Composing the swath of development tools for all supported
              languages into a single Docker image became unwieldy.
       
                * Polygott provides a bunch of scripts and config files to
                  install all the tools needed for each languages, but these
                  become painful to maintain.
       
                * No matter what we tried, it was nearly impossible to share
                  layers between multiple builds of Polygott. Polygott itself is
                  20-30GB which adds up quickly when multiple versions are in
                  production at the same time.
       
          Using Nix, we now give users the power to install the tools they need
          rather than static set of predefined tools. At the same time it
          addresses the problems above and reduces our internal maintenance
          burden. Win-win!
       
          While we are jumping on the Nix train, we aren't dumping Docker
          containers. We still need containers to provide each repl with an
          isolated environment.
       
          Will Nix overtake Docker? No, these tools accomplish different goals,
          however they can be used in combination to provide the best of both
          worlds: reproducible builds and containerized deployments.
       
          Are you interested in working with Nix or are you fascinated by
          large-scale container orchestration? [25]Come work with us!
       
          ---------------------------------------------------------------------
       
          1 All packages in Nix aren't 100% reproducible, but work is being done
          to improve this. Additionally, each machine may have its own version
          of [26]nixpkgs. For maximum reproduciblility, nixpkgs should be [27]pinned.
          This will happen by default with [28]Nix flakes
       
          Like what you read? You can
       
          [29] Twitter Logo Follow @Replit on Twitter!
       
          Legal
       
          [30]Terms and services [31]Privacy [32]Subprocessors [33]DPA [34]US
          Student DPA
       
          Replit
       
          [8]Blog [35]About [3]Jobs [36]Teams [5]Pricing
       
          Handy links
       
          [37]Create a repl [38]Docs [39]Feedback [40]Bug reports [41]Language
          requests [42]Status page
       
          Social media
       
          [43]Facebook [29]Twitter [44]Instagram [45]Discord
       
          Languages
       
          [46]Clojure [47]Haskell [48]Kotlin (beta) [49]QBasic [50]Forth [51]LOLCODE
          [52]BrainF [53]Emoticon [54]Bloop [55]Unlambda [56]JavaScript [57]CoffeeScript
          [58]Scheme [59]APL [60]Lua [61]Python 2.7 [62]Ruby [63]Roy [64]Python
          [65]Nodejs [66]Enzyme [67]Go [68]C++ [69]C++11 [70]C [71]C# [72]F#
          [73]HTML, CSS, JS [74]Rust [75]Swift [76]Python (with Turtle) [77]Jest
          [78]Django [79]Express [80]Sinatra [81]Ruby on Rails [82]R [83]Next.js
          [84]GatsbyJS [85]React [86]React Typescript [87]React Reason [88]Bash
          [89]Quil [90]Crystal [91]Julia [92]Elixir [93]Nim [94]Reason NodeJs
          [95]Erlang [96]TypeScript [97]Pygame [98]Love2D [99]Tkinter [100]Java
          Swing [101]Emacs Lisp (Elisp) [102]PHP Web Server [103]SQLite [104]Java
          [105]PHP CLI [106]Pyxel
       
          Copyright © 2021 Replit, Inc. All rights reserved.
       
          
       
          1. https://replit.com
          2. https://replit.com/site/ide
          3. https://replit.com/site/jobs
          4. https://blog.replit.com
          5. https://replit.com/site/pricing
          6. https://replit.com/jam?from=static
          7. https://replit.com/signup
          8. https://blog.replit.com/
          9. https://blog.replit.com/__repl?fileName=posts/nix-vs-docker.md
          10. https://blog.replit.com/nix-vs-docker
          11. https://nixos.org/
          12. https://www.docker.com/
          13. https://news.ycombinator.com/item?id=22295740
          14. https://news.ycombinator.com/item?id=23253856
          15. https://news.ycombinator.com/item?id=28734457
          16. https://docs.docker.com/get-started/02_our_app/
          17. https://en.wikipedia.org/wiki/Union_mount
          18. https://hub.docker.com/
          19. https://xkcd.com/1172/
          20. https://www.hyrumslaw.com/
          21. https://en.wikipedia.org/wiki/First_principle
          22. https://github.com/replit/Polygott
          23. https://blog.replit.com/nix
          24. https://replit.com/apps/nix
          25. https://replit.com/site/careers
          26. https://github.com/NixOS/nixpkgs
          27. https://nixos.wiki/wiki/FAQ/Pinning_Nixpkgs
          28. https://nixos.wiki/wiki/Flakes
          29. https://twitter.com/replit
          30. https://replit.com/site/terms
          31. https://replit.com/site/privacy
          32. https://replit.com/site/subprocessors
          33. https://replit.com/site/dpa
          34. https://docs.replit.com/Teams/US_Student_DPA
          35. https://replit.com/about
          36. https://replit.com/site/teams
          37. https://replit.com/languages
          38. https://replit.com/site/docs
          39. https://replit.com/feedback
          40. https://replit.com/bugs
          41. https://replit.com/language-requests
          42. https://status.replit.com
          43. https://facebook.com/repl.it
          44. https://instagram.com/repl.it
          45. https://repl.it/discord
          46. https://replit.com/languages/clojure
          47. https://replit.com/languages/haskell
          48. https://replit.com/languages/kotlin
          49. https://replit.com/languages/qbasic
          50. https://replit.com/languages/forth
          51. https://replit.com/languages/lolcode
          52. https://replit.com/languages/brainfuck
          53. https://replit.com/languages/emoticon
          54. https://replit.com/languages/bloop
          55. https://replit.com/languages/unlambda
          56. https://replit.com/languages/javascript
          57. https://replit.com/languages/coffeescript
          58. https://replit.com/languages/scheme
          59. https://replit.com/languages/apl
          60. https://replit.com/languages/lua
          61. https://replit.com/languages/python
          62. https://replit.com/languages/ruby
          63. https://replit.com/languages/roy
          64. https://replit.com/languages/python3
          65. https://replit.com/languages/nodejs
          66. https://replit.com/languages/enzyme
          67. https://replit.com/languages/go
          68. https://replit.com/languages/cpp
          69. https://replit.com/languages/cpp11
          70. https://replit.com/languages/c
          71. https://replit.com/languages/csharp
          72. https://replit.com/languages/fsharp
          73. https://replit.com/languages/html
          74. https://replit.com/languages/rust
          75. https://replit.com/languages/swift
          76. https://replit.com/languages/python_turtle
          77. https://replit.com/languages/jest
          78. https://replit.com/languages/django
          79. https://replit.com/languages/express
          80. https://replit.com/languages/sinatra
          81. https://replit.com/languages/rails
          82. https://replit.com/languages/rlang
          83. https://replit.com/languages/nextjs
          84. https://replit.com/languages/gatsbyjs
          85. https://replit.com/languages/reactjs
          86. https://replit.com/languages/reactts
          87. https://replit.com/languages/reactre
          88. https://replit.com/languages/bash
          89. https://replit.com/languages/quil
          90. https://replit.com/languages/crystal
          91. https://replit.com/languages/julia
          92. https://replit.com/languages/elixir
          93. https://replit.com/languages/nim
          94. https://replit.com/languages/reason_nodejs
          95. https://replit.com/languages/erlang
          96. https://replit.com/languages/typescript
          97. https://replit.com/languages/pygame
          98. https://replit.com/languages/love2d
          99. https://replit.com/languages/tkinter
          100. https://replit.com/languages/java_swing
          101. https://replit.com/languages/elisp
          102. https://replit.com/languages/php7
          103. https://replit.com/languages/sqlite
          104. https://replit.com/languages/java10
          105. https://replit.com/languages/php_cli
          106. https://replit.com/languages/pyxel