<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>LogN</title>
    <link rel="self" type="application/atom+xml" href="https://logandevine.com/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://logandevine.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-09-22T00:00:00+00:00</updated>
    <id>https://logandevine.com/atom.xml</id>
    <entry xml:lang="en">
        <title>Inside Digital Cinema Security Architecture</title>
        <published>2026-09-22T00:00:00+00:00</published>
        <updated>2026-09-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            Logan Devine
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://logandevine.com/posts/dci-security/"/>
        <id>https://logandevine.com/posts/dci-security/</id>
        
        <content type="html" xml:base="https://logandevine.com/posts/dci-security/">&lt;p&gt;&lt;em&gt;Avengers: Doomsday&lt;&#x2F;em&gt; is reported to have a production budget of over $400 million. When you watch a movie at home, the task is quite simple: decrypt the content and play it on a screen. In digital cinema, the stakes are much higher: full-resolution theatrical masters are delivered to theaters days in advance, where they sit on storage, are transferred to projectors, and eventually played back before the films are generally available.&lt;&#x2F;p&gt;
&lt;p&gt;That creates an unusual security problem. In the music industry, record stores are common sources of leaks, when employees receive early access to shipments of a particular album. So, how do studios provide exhibitors with the ability to play back a movie, while preventing unauthorized copying?&lt;&#x2F;p&gt;
&lt;p&gt;Simply encrypting the film at rest doesn&#x27;t suffice. The security model in question treats the equipment in the projection booth and the cinema operators themselves as untrusted. The security therefore protects the content from when it is stored at rest, all the way to when it is physically projected on a screen.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-digital-cinema-initiatives&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-digital-cinema-initiatives&quot; aria-label=&quot;Anchor link for: the-digital-cinema-initiatives&quot;&gt;The Digital Cinema Initiatives&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;In 2002, a group of &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Digital_Cinema_Initiatives#:~:text=The%20organization%20was%20formed%20in%20March%202002&quot;&gt;seven major motion picture studios&lt;&#x2F;a&gt; came together to form a coalition known as Digital Cinema Initiatives, LLC. The primary purpose of this coalition was to produce a series of open standards that would ensure interoperability amongst studios and various digital cinema vendors. Their primary work, the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;documents.dcimovies.com&#x2F;DCSS&#x2F;draft&#x2F;latest&#x2F;Digital-Cinema-System-Specification.pdf&quot;&gt;Digital Cinema System Specification&lt;&#x2F;a&gt;, more commonly referred to as the &quot;DCI specification,&quot; covers a series of basic technical and quality requirements to ensure consistency across tens of thousands of cinemas worldwide.&lt;&#x2F;p&gt;
&lt;p&gt;Out of this very long standard, we are interested in one thing in particular: how does the DCI spec keep content secure, when it eventually &lt;em&gt;must&lt;&#x2F;em&gt; become plaintext to be turned into light?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-journey-to-playback&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-journey-to-playback&quot; aria-label=&quot;Anchor link for: the-journey-to-playback&quot;&gt;The Journey to Playback&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;The following diagram is just a glimpse at the overall roles in the DCI security process.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;logandevine.com&#x2F;posts&#x2F;dci-security&#x2F;.&#x2F;overview.svg&quot; alt=&quot;A flowchart showing content moving through a series of steps: digital cinema package delivery, local content library, projector storage, media decryption, forensic marking, link encryption&#x2F;decryption, and finally playback.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As you can see, this process is far different than the traditional DRM one might see with streaming. So, let&#x27;s follow the valuable plaintext master through all of the steps in this diagram, up to when it is finally projected to an audience.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;receiving-the-content&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#receiving-the-content&quot; aria-label=&quot;Anchor link for: receiving-the-content&quot;&gt;Receiving the Content&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Unlike the old days where cinemas received trucks and had to unload reels of film, digital cinemas receive films in the form of DCPs, or Digital Cinema Packages. These large folders often are delivered by whatever mechanism the distributor desires: many come over satellite, via the standard Internet, and sometimes via a specialized hard drive.&lt;&#x2F;p&gt;
&lt;p&gt;DCPs are not simple video containers like MPEG4, they are in fact directories containing primarily XML files and MXF containers. These MXF containers contain Linear PCM audio and JPEG-2000 frames for video.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;logandevine.com&#x2F;posts&#x2F;dci-security&#x2F;.&#x2F;dcp-contents.png&quot; alt=&quot;A sample of a DCP&amp;#39;s contents, containing 4 XML files and 2 MXF containers&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is the structure of a simple DCP. Inside, there is a volume index (VOLINDEX.xml), an asset map (ASSETMAP.xml), a Packing List (pkl_...) and a Composition Playlist (cpl_...). These 4 XML files inevitably reference the 2 MXF &quot;reels&quot; containing both the video and audio &quot;essences&quot; of the DCP.&lt;&#x2F;p&gt;
&lt;div class=&quot;note-container&quot;&gt;
    
            &lt;div class=&quot;note-header&quot;&gt;
                
                    &lt;div class=&quot;note-icon&quot;&gt;
                        &lt;p&gt;Notice the folder&#x27;s name?&lt;&#x2F;p&gt;

                    &lt;&#x2F;div&gt;
                
            &lt;&#x2F;div&gt;
            &lt;div class=&quot;note-content&quot;&gt;
                &lt;p&gt;Yeah, they&#x27;re &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.isdcf.com&#x2F;registry&#x2F;illustratedguide&#x2F;&quot;&gt;actually that bad&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The file name alone contains almost all of the human facing metadata, like audio language and aspect ratio. It&#x27;s horrible.&lt;&#x2F;p&gt;

            &lt;&#x2F;div&gt;
        
    &lt;&#x2F;div&gt;
&lt;h1 id=&quot;safety-at-rest&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#safety-at-rest&quot; aria-label=&quot;Anchor link for: safety-at-rest&quot;&gt;Safety at Rest&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;The first layer of protection defends against the most obvious attack: extracting the content of the DCP directly once it&#x27;s received. This is solved through DCP encryption, which encrypts the contents of the video containers using the symmetric cipher AES-128-CBC.&lt;&#x2F;p&gt;
&lt;p&gt;AES-128 was chosen here by DCI because of the sheer amount of data that a DCP might contain (many larger feature films are upwards of 300-400GB!). As we will soon learn, this data is decrypted realtime by a server attached to the projector.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;but-where-are-the-keys&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#but-where-are-the-keys&quot; aria-label=&quot;Anchor link for: but-where-are-the-keys&quot;&gt;But where are the keys?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A naive approach might be to give the cinema access to the keys when they are first allowed to screen the film. This provides security against early unauthorized access, but once the cinema has the keys, they can then extract the content. Historically, when film reels were in use, if an exhibitor refused to return their content the studio would need to sue the theater to get their prints back. One of the things studios desire to protect against is the unauthorized playback of their content to an audience too early (before a release) or too late (after the exhibitor&#x27;s booking has expired).&lt;&#x2F;p&gt;
&lt;p&gt;To provide this guarantee, these AES keys are exchanged through the use of Key Delivery Messages (KDMs) delivered out-of-band. These KDMs are delivered to the cinema a few days in advance of the screening, after the distributor has confirmed the booking, often delivered via a simple email.&lt;&#x2F;p&gt;
&lt;p&gt;Inside these emails, you&#x27;ll find a &lt;code&gt;.kdm.xml&lt;&#x2F;code&gt; file, which contains information about a specific clip that it authorizes, a &quot;ValidAfter&quot; and &quot;ValidBefore&quot; timestamp, and finally an RSA-2048 encrypted payload. But for who, you might ask?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;screen-servers&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#screen-servers&quot; aria-label=&quot;Anchor link for: screen-servers&quot;&gt;Screen Servers&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Every projector has an adjacent server, which may be integrated directly into a card in the projector&#x27;s head-end (as with newer Integrated Media Block modules) or a discrete rack-mounted unit. Many vendors make these systems (e.g. Dolby, Christie, GDC) and they are often integrated into a full suite of projection management hardware and software.&lt;&#x2F;p&gt;
&lt;p&gt;These servers, known as &lt;strong&gt;screen servers&lt;&#x2F;strong&gt;, contain all of the necessary functions for content transfer, playback, scheduling, management, and GPIO. They receive content, commands, and those very Key Delivery Messages for processing.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;logandevine.com&#x2F;posts&#x2F;dci-security&#x2F;.&#x2F;screen-server.svg&quot; alt=&quot;A diagram of components in a screen server, including the dedicated storage array, secure processing block, and screen management system&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;securing-the-kdm&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#securing-the-kdm&quot; aria-label=&quot;Anchor link for: securing-the-kdm&quot;&gt;Securing the KDM&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The key delivery message itself contains an RSA-2048 encrypted payload, which is encrypted directly to the public key of the screen server in question. Inside this RSA message is the actual symmetric key used to decrypt the AES blocks.&lt;&#x2F;p&gt;
&lt;p&gt;This allows distributors to ensure that their film is only played back on an authorized screen, by certified hardware. However, it is important that this hardware be field-servicable (as it often includes a large disc array), while still ensuring that the physical chips storing the screen server&#x27;s RSA private key are kept safe from tampering.&lt;&#x2F;p&gt;
&lt;p&gt;That security is the role of the Secure Processing Block. These blocks are typically PCI cards, or otherwise tightly integrated components including what DCI calls &quot;Secure Silicon.&quot; The specification mandates components be held up to an adjusted version of the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nvlpubs.nist.gov&#x2F;nistpubs&#x2F;FIPS&#x2F;NIST.FIPS.140-2.pdf&quot;&gt;FIPS 140-2&lt;&#x2F;a&gt; standard&#x27;s third compliance level, which mandates both tamper evident designs and active efforts to prevent the extraction of contained cryptographic key material and certificates.&lt;&#x2F;p&gt;
&lt;p&gt;For most secure silicon designs, this involves using tamper-resistent ICs, and often burying a web of sensitive traces into a solid, opaque block. If any of these security measures are broken, the cryptographic modules embedded within the silicon will erase themselves, effectively bricking themselves. These designs are often also dependent on battery power: projectionists regularly check battery voltages to prevent the server from losing its private key and bricking itself.&lt;&#x2F;p&gt;
&lt;p&gt;At this point, the movie is encrypted at rest, and the keys that can decrypt it are only able to be read by the specific projector. But what happens inside of the screen server&#x27;s SPB at playback time?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;playback-and-decryption&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#playback-and-decryption&quot; aria-label=&quot;Anchor link for: playback-and-decryption&quot;&gt;Playback and Decryption&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;logandevine.com&#x2F;posts&#x2F;dci-security&#x2F;.&#x2F;server-spb.svg&quot; alt=&quot;The contents of the screen server&amp;#39;s secure silicon: a security manager, content decryptors, a secure clock, and a link encryptor.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Inside of this secure silicon, you&#x27;ll find the Security Manager (SM), which manages the actual cryptographic key material of the screen server. It provides the Content Decryptors with the AES keys required to decrypt the actual content, and also has a secure clock to authorize the KDM&#x27;s validity.&lt;&#x2F;p&gt;
&lt;p&gt;To solve the problem of a cinema holding a film longer than intended, this secure module will trust its own clock from the factory, which cannot be adjusted by the exhibitor any more than 7 minutes per year. It is expected to stay as close to proper UTC as possible, and it is how the Security Manager decides if a KDM can be decrypted.&lt;&#x2F;p&gt;
&lt;p&gt;If the KDM is no longer or not yet valid based on its provided timestamp, the SM will refuse to play back the show entirely.&lt;&#x2F;p&gt;
&lt;p&gt;At this point, the individual &quot;essence&quot; (audio, video, subtitles) has been decrypted, with the entirety of it remaining within the top physical security of the Secure Processing Block. Now that we&#x27;ve solved the problems of validity times, we must worry about where the ciphertext goes next. In this case, that&#x27;s directly into the &lt;strong&gt;forensic marker&lt;&#x2F;strong&gt; (FM).&lt;&#x2F;p&gt;
&lt;h1 id=&quot;forensic-marking&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#forensic-marking&quot; aria-label=&quot;Anchor link for: forensic-marking&quot;&gt;Forensic Marking&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;The DCI standard acknowledges that it is impossible to fully secure the content once it is in the final stages of the projector or once it&#x27;s on the screen. To solve this, DCI compliant media servers are required to inject a forensic mark into most encrypted content they play back, identifying the current time and its serial number (and hence the location and auditorium).&lt;&#x2F;p&gt;
&lt;p&gt;The specifics of this architecture are proprietary to the individual IMB manufacturer, but they are invisible marks encoding this very information typically into both audio and video. A very common solution is Thomson (Technicolor)&#x27;s NexGuard forensic mark, but these specific solutions are kept proprietary to prevent reverse engineering and potential removal.&lt;&#x2F;p&gt;
&lt;p&gt;Once the content has been marked, still within the SPB, it now needs to leave this secure region to travel to the projector&#x27;s head-end.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;link-encryption&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#link-encryption&quot; aria-label=&quot;Anchor link for: link-encryption&quot;&gt;Link Encryption&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;In older discrete media server installations, which are still common in many cinemas worldwide, the data must travel from a separate rack-mounted Screen Server into the projector before it can be projected.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;logandevine.com&#x2F;posts&#x2F;dci-security&#x2F;.&#x2F;spb-links.svg&quot; alt=&quot;A diagram depicting the link between two Secure Processing Blocks. A marriage&#x2F;TLS link connects the Link Encryptor to the projector&amp;#39;s Link Decryptor, before it flows into the Light Engine, audio outputs, and Lens.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;div class=&quot;note-container&quot;&gt;
    
            &lt;div class=&quot;note-header&quot;&gt;
                
                    &lt;div class=&quot;note-icon&quot;&gt;
                        &lt;p&gt;Integrated Media Blocks&lt;&#x2F;p&gt;

                    &lt;&#x2F;div&gt;
                
            &lt;&#x2F;div&gt;
            &lt;div class=&quot;note-content&quot;&gt;
                &lt;p&gt;In newer installations, all of this functionality is integrated into one card often embedded directly into the projector. In that case, it is all integrated into one Secure Processing Block, with no link encryptor and no marriage mechanism. For those scenarios, the standard still requires the same physical access controls.&lt;&#x2F;p&gt;

            &lt;&#x2F;div&gt;
        
    &lt;&#x2F;div&gt;
&lt;p&gt;To prevent interception of this link, the two SPBs (screen server and projector) must be &quot;married.&quot; This prevents interception along a physical connection between these two different components. If this connection is broken or serviced for any reason, the two components will endure a very difficult divorce.&lt;&#x2F;p&gt;
&lt;p&gt;To marry these two components, an authorized technician of the manufacturer is required, who has the ability to inspect the connection and bless the marriage. This link is also established using digital certificates issued to authorize DCI-compliant devices by their manufacturer.&lt;&#x2F;p&gt;
&lt;p&gt;The SPB in the projector, however, is not held to the same security standards as the screen server, as it&#x27;s often required to be far more servicable.&lt;&#x2F;p&gt;
&lt;p&gt;This marriage will be broken if the projector SPB detects any form of tampering, or if manually requested by a technician. This marriage also has a third state, for &quot;maintenance,&quot; which allows projector manufacturers to provide a maintenance access door into the important signal chain components (such as the light engine) while preventing someone from, for example, soldering directly onto the board to receive signals. It is still required that the private key for the certificate associated with the projector is kept on the same Secure Silicon, to prevent that from being reused to MITM it.&lt;&#x2F;p&gt;
&lt;p&gt;So, at this point, the data has passed over a trusted, inspected (and authorized with PKI) link into a secured region of the projector. If any access doors into this portion of the projector are open (the maintenance state), the projector is required to inhibit playback.&lt;&#x2F;p&gt;
&lt;p&gt;Here, the link to the projector is decrypted, and the plaintext (but marked) video essence is passed directly to the projector&#x27;s internal components.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;audio&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#audio&quot; aria-label=&quot;Anchor link for: audio&quot;&gt;Audio&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;The DCI standard does not treat audio as something that must be secured up to the speaker. If they did, you would need specialized, certified &quot;d-cinema speakers&quot; with the ability to decrypt directly to a secure region. So, after the Link Decryptor decrypts the contents in the head of the projector, there are typically direct plain outputs for audio, subtitles, etc.&lt;&#x2F;p&gt;
&lt;p&gt;The philosophy here assumes that audio is far less valuable in the creation of a bootleg copy of a film, which would be true. The protection all the way up to the LD still stands.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;logging&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#logging&quot; aria-label=&quot;Anchor link for: logging&quot;&gt;Logging&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;This implements the &quot;control lightly&quot; portion of the standard&#x27;s security model, but as with all industries the audit requirements are far stronger. The specification requires the Security Manager inside the screen server to also preserve at least a year of logs for every clip&#x27;s start and end, security actions (like the projector&#x27;s service door opening and closing), etc. in a non-volatile ring buffer.&lt;&#x2F;p&gt;
&lt;p&gt;These logs are often published over the network (for local tools&#x27; consumption) and also stored internally, with cryptographic signatures tying them back to the specific SM which produced them.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;recap&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#recap&quot; aria-label=&quot;Anchor link for: recap&quot;&gt;Recap&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;A DCP is delivered to the cinema containing encrypted contents, sometimes weeks before the screening&lt;&#x2F;li&gt;
&lt;li&gt;A KDM (Key Delivery Message) is sent to the cinema a few days before the showings are scheduled to start, containing contents encrypted to a specific screen server.&lt;&#x2F;li&gt;
&lt;li&gt;Physically secure hardware modules within the screen server&#x27;s secure silicon (capable of erasing themselves at a moment&#x27;s notice) hold the private key required to decrypt that KDM and issue the secrets required to decrypt the actual AES blocks of the film.&lt;&#x2F;li&gt;
&lt;li&gt;This content is overlayed with a forensic mark, preventing camcorder usage or extraction later in the process.&lt;&#x2F;li&gt;
&lt;li&gt;These marked feeds are encrypted over a physically trusted and certificate authorized TLS link to another secured segment of the projector itself.&lt;&#x2F;li&gt;
&lt;li&gt;If any access hatches are opened on the projector, content playback is stopped to prevent physically accessing the data as it is prepared to become light&lt;&#x2F;li&gt;
&lt;li&gt;A comprehensive audit log is preserved of all security actions, including what is played back and when&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All in all, this security model shows just a glimpse of the amount of work the major studios are putting in to help preserve their valuable master copies and pre-release assets. This architecture in specific shows how just a small set of cryptographic primitives (RSA-2048, AES-128-CBC, TLS, PKI models, secure silicon, and even plain old physical inspection) can come together to form an incredibly secure system.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Who Gets The Fast Path?</title>
        <published>2026-08-29T00:00:00+00:00</published>
        <updated>2026-08-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            Logan Devine
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://logandevine.com/posts/who-gets-the-fast-path/"/>
        <id>https://logandevine.com/posts/who-gets-the-fast-path/</id>
        
        <content type="html" xml:base="https://logandevine.com/posts/who-gets-the-fast-path/">&lt;p&gt;I&#x27;ve been working on my hobby compiler &lt;a href=&quot;&#x2F;projects&#x2F;zirco&#x2F;&quot;&gt;Zirco&lt;&#x2F;a&gt; for the last few years, and one of its selling points for me is raw compile-time speed. Naive benchmarks show &lt;code&gt;zrc&lt;&#x2F;code&gt; compiles code about 4x faster than &lt;code&gt;clang&lt;&#x2F;code&gt; and &lt;code&gt;gcc&lt;&#x2F;code&gt;, while still using LLVM to produce binaries within 10% of identical C code.&lt;&#x2F;p&gt;
&lt;p&gt;This is ironic, considering the zrc codebase is not designed great by any measure: the codebase is full of deeply nested structures and liberal allocation. Fortunately, optimization can be external to the program itself. Alongside many other projects, we build the Rust code with aggressive optimization, a single codegen unit, and fat LTO. Naturally the next step is to make zrc &lt;em&gt;even faster&lt;&#x2F;em&gt; using Profile Guided Optimization (PGO) and BOLT.&lt;&#x2F;p&gt;
&lt;p&gt;PGO removes a lot of guesswork from optimizations. Instead of guessing which parts of the codebase run regularly (the &quot;hot&quot; functions or branches), instrumentation and benchmarking is used to collect data on exactly how the program executes against some workload. The compiler can use that to optimize for those specific code paths, and you now have faster code with less guesswork.&lt;&#x2F;p&gt;
&lt;p&gt;For a compiler however, determining what counts as an appropriate workload is the hard part.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;follow-the-fast-path&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#follow-the-fast-path&quot; aria-label=&quot;Anchor link for: follow-the-fast-path&quot;&gt;Follow the fast path&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;For the computational beasts they are, CPUs are shockingly good at guessing. Almost all modern CPUs contain mechanisms for branch prediction and speculative execution (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Spectre_(security_vulnerability)&quot;&gt;as good as they may be&lt;&#x2F;a&gt;). Compilers are aware of this, and perform various optimizations so that the CPU is executing common instructions close together, often even linearly. This allows streamlined execution which reduces costly cache misses and mispredictions.&lt;&#x2F;p&gt;
&lt;p&gt;The next step up is to hint to the compiler what branches are frequent: Rust has the &lt;code&gt;likely_unlikely&lt;&#x2F;code&gt; experiment which provides functions like &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;hint&#x2F;fn.likely.html&quot;&gt;&lt;code&gt;std::hint::likely&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, and has stable support for &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;hint&#x2F;fn.cold_path.html&quot;&gt;&lt;code&gt;std::hint::cold_path&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and the &lt;code&gt;#[cold]&lt;&#x2F;code&gt; function attribute. Many C compilers similarly support &lt;code&gt;__builtin_expect&lt;&#x2F;code&gt;, and Zirco even has &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;book.zirco.dev&#x2F;reference.html#615-unreachable-statements&quot;&gt;the &lt;code&gt;unreachable&lt;&#x2F;code&gt; keyword&lt;&#x2F;a&gt; to indicate a section of unreachable code.&lt;&#x2F;p&gt;
&lt;p&gt;The difference can be substantial, but these optimizations should not be applied without proper benchmark data to back them, as they can make code less readable and even harm performance if used incorrectly. In the case that the compiler is not adequately guessing the runtime behavior of your code, PGO allows the compiler to use actual profiling information instead of static analysis to determine the hot path. BOLT takes this a step further by rearranging the final compiled binary in a third step, pushing rarely used functions further down the binary.&lt;&#x2F;p&gt;
&lt;p&gt;The difference can be substantial: enabling PGO&#x2F;BOLT in Zirco produced roughly a 20% improvement in compilation speed.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;runtime-information-as-a-datasource&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#runtime-information-as-a-datasource&quot; aria-label=&quot;Anchor link for: runtime-information-as-a-datasource&quot;&gt;Runtime information as a datasource&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Static analysis can only do so much.&lt;&#x2F;p&gt;
&lt;p&gt;PGO changes that: you build an &quot;instrumented&quot; binary, run it on a workload (say, a benchmark or test suite) and then recompile the code using generated profiling information. For a simple web service, this is pretty easy: the &quot;representative workload&quot; is real user traffic, and the profiles can even be collected directly from a production server.&lt;&#x2F;p&gt;
&lt;p&gt;The question for Zirco however was, what counts as a representative workload? This is far less obvious. You could use a specifically designed (or even generated) set of programs that target the most complex portions of the compilation pipeline. You could compile Zirco&#x27;s standard library. You could run the test suite. All of these target something in specific.&lt;&#x2F;p&gt;
&lt;p&gt;The problem is similar to &quot;overfitting&quot; in machine learning. The moment you are optimizing using PGO, you&#x27;re biasing runtime speed to a specific target audience. Using the test suite for example, might bias optimizations in favor of small programs and startup code, as unit tests tend to be.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;there-isn-t-one-workload&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#there-isn-t-one-workload&quot; aria-label=&quot;Anchor link for: there-isn-t-one-workload&quot;&gt;There isn&#x27;t one workload&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Optimization is no longer deterministic transformation, it&#x27;s now a product decision. &lt;em&gt;Who do I want to benefit most?&lt;&#x2F;em&gt; And not just that, &lt;em&gt;who will I allow to incur slowdowns as a result of this optimization?&lt;&#x2F;em&gt; Behavior differs between developers and environments: some might be compiling regularly as a result of a LSP integration, one might be building in CI, and one might just be writing small example programs to try the language out.&lt;&#x2F;p&gt;
&lt;p&gt;These are all valid use cases, and the issue is, not everyone wins.&lt;&#x2F;p&gt;
&lt;p&gt;For zrc, I started out by using the examples, the test suite, and the standard library. I felt like this gave adequate coverage: a mix of complete programs (the larger examples like &lt;code&gt;calculator&lt;&#x2F;code&gt;), smaller samples meant to demonstrate specific language features, alongside coverage of all of the language&#x27;s core functions. This will vary from codebase to codebase, so use benchmarks to determine what workloads are sufficient.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;how-i-actually-implemented-pgo-bolt&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-i-actually-implemented-pgo-bolt&quot; aria-label=&quot;Anchor link for: how-i-actually-implemented-pgo-bolt&quot;&gt;How I actually implemented PGO&#x2F;BOLT&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zirco-lang&#x2F;zrc&#x2F;pull&#x2F;776&quot;&gt;PR #776&lt;&#x2F;a&gt; enabled PGO, using &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kobzol&#x2F;cargo-pgo&quot;&gt;cargo-pgo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Using that tool, you can build instrumented builds and run them on your workload, before building a final PGO-optimized binary:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; cargo pgo instrument build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; cargo pgo instrument test&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;font-style: italic;&quot;&gt;  # run the test suite&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; .&#x2F;target&#x2F;x86_64-unknown-linux-gnu&#x2F;release&#x2F;zrc .&#x2F;work&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; cargo pgo optimize build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And for BOLT, that last final build is replaced with a &lt;em&gt;second&lt;&#x2F;em&gt; BOLT optimization stage:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; cargo pgo bolt build --with-pgo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; .&#x2F;target&#x2F;x86_64-unknown-linux-gnu&#x2F;release&#x2F;zrc-bolt-instrumented .&#x2F;work&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; cargo pgo bolt optimize --with-pgo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;ci-has-become-a-factory&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ci-has-become-a-factory&quot; aria-label=&quot;Anchor link for: ci-has-become-a-factory&quot;&gt;CI has become a factory&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;With a full PGO+BOLT release build, the code is now compiled 3 times. Once to instrument for PGO, once to build a PGO-optimized binary instrumented for BOLT, and once again to build the final binary. I consider this worthwhile for the 20% speed boost, but this meant my slowest release build (Intel macOS) had tripled in length, bringing my pipeline up to 23 minutes. For a simple PR, I felt this was unacceptable, so I altered my pipeline to only build with these heavy optimizations for nightly builds and final releases, instead of as part of the normal CI pipeline.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;a-note-on-benchmarking&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-note-on-benchmarking&quot; aria-label=&quot;Anchor link for: a-note-on-benchmarking&quot;&gt;A note on benchmarking&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Remember the overfitting problem from earlier? The same is true with your benchmarks. Don&#x27;t let your PGO workload be the same as your benchmarks: it may overestimate just how much improvement you&#x27;re getting. In fact, if you press too hard to optimize against your benchmark, you might even slow things down for the average user. The 20% measurement in speed increase was done on something entirely different than the examples and test suite we optimize for, as it was a full length program for a prime number sieve.&lt;&#x2F;p&gt;
&lt;p&gt;It is easy to mislead yourself and your team: be careful as to what&#x27;s being benchmarked and make sure you have some real user benchmarks that aren&#x27;t in your profiling workload.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;who-gets-the-fast-path&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#who-gets-the-fast-path&quot; aria-label=&quot;Anchor link for: who-gets-the-fast-path&quot;&gt;Who gets the fast path?&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Ultimately, this is what makes PGO so interesting (and difficult) to adopt. Unlike mechanical optimizations such as most loop unrolling or function inlining, it requires choosing exactly who your program is for, and intentionally biasing performance in that direction.&lt;&#x2F;p&gt;
&lt;p&gt;There is no one fast path, only those which you decided were worth speeding up.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Debian + Nix: Stability Meets Reproducibility</title>
        <published>2026-08-05T00:00:00+00:00</published>
        <updated>2026-08-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            Logan Devine
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://logandevine.com/posts/debian-plus-nix-stability-meets-reproducibility/"/>
        <id>https://logandevine.com/posts/debian-plus-nix-stability-meets-reproducibility/</id>
        
        <content type="html" xml:base="https://logandevine.com/posts/debian-plus-nix-stability-meets-reproducibility/">&lt;p&gt;By now, most people have heard about NixOS. &quot;I&#x27;m using Nix&quot; has become the very flex that &quot;I use arch btw&quot; had always strived to be, with none of the meme.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been using Debian for years. I run it everywhere, from my desktop to all of my production servers. It brings a familiar, slow-changing system, but the point-release model comes with a major downside for development: you are often working with years old toolchains.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;llvm-19&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#llvm-19&quot; aria-label=&quot;Anchor link for: llvm-19&quot;&gt;LLVM 19!?&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;This issue first arose for me when I was working on &lt;a href=&quot;&#x2F;projects&#x2F;zirco&#x2F;&quot;&gt;Zirco&lt;&#x2F;a&gt;, which currently pins LLVM 22:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;dependencies.inkwell&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;0.9.0&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;features&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt;    &amp;quot;llvm22-1&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt;    &amp;quot;llvm22-1-prefer-static&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The Debian Trixie repositories however, only ship LLVM from 17 to 19:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; apt list -a &amp;#39;llvm-*-dev&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;llvm-17-dev&#x2F;stable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; 1:17.0.6-22+b2 amd64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;llvm-18-dev&#x2F;stable&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; 1:18.1.8-18+b1 amd64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;llvm-19-dev&#x2F;stable,now&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; 1:19.1.7-3+b1 amd64&lt;&#x2F;span&gt;&lt;span&gt; [installed,automatic]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Well... LLVM doesn&#x27;t provide many great options for system-wide installations here. There is a stage2 apt repository available, but I had various library issues with that in the past. So, I reached for one of the tools I had heard quite a bit about: Nix.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;wait-nix-without-nixos&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#wait-nix-without-nixos&quot; aria-label=&quot;Anchor link for: wait-nix-without-nixos&quot;&gt;Wait, Nix without NixOS?&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Many people forget that NixOS is a Linux distribution &lt;em&gt;built around&lt;&#x2F;em&gt; the Nix package manager, which is a great tool and can be used standalone on any operating system (even macOS and Windows)!&lt;&#x2F;p&gt;
&lt;p&gt;Nix is fundamentally built around being a reproducible build system, ensuring that it produces software, environments, or even full operating system configurations that are identical across machines.&lt;&#x2F;p&gt;
&lt;p&gt;This solves one particular issue every developer will be familiar with:&lt;&#x2F;p&gt;
&lt;h1 id=&quot;it-works-on-my-machine&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#it-works-on-my-machine&quot; aria-label=&quot;Anchor link for: it-works-on-my-machine&quot;&gt;It works on my machine!&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;The classic excuse, and yet a real problem. The declarative nature of Nix helps solve this issue: one can define their entire development environment (packages, libraries, their versions, and even environment variables) in a single file, and share it with others working on the project. This
ensures that for anyone working on the project, the environment is identical, and therefore eliminates fighting to find missing libraries or other mismatches between contributors.&lt;&#x2F;p&gt;
&lt;p&gt;There are some caveats here, such as environment provided by the host operating system, but these issues are generally far less severe than what you&#x27;d encounter with a traditional package manager.&lt;&#x2F;p&gt;
&lt;p&gt;You may ask, if you are using Nix, why not just use NixOS? Well, I like Debian. I like when repos move slowly, except for when I need bleeding edge development tools. Nix gives me the best of both worlds: A system which is stable, and &lt;strong&gt;temporary&lt;&#x2F;strong&gt; environments when I need something newer.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you already know Nix, I recommend skipping to &lt;a href=&quot;https:&#x2F;&#x2F;logandevine.com&#x2F;posts&#x2F;debian-plus-nix-stability-meets-reproducibility&#x2F;#the-real-benefits&quot;&gt;The real benefits&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;So, now that you&#x27;ve seen the benefits of running Nix, let&#x27;s get a simple development environment (with LLVM 22, of course) up and running on Debian!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;installing-nix&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#installing-nix&quot; aria-label=&quot;Anchor link for: installing-nix&quot;&gt;Installing Nix&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Follow the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;download&#x2F;&quot;&gt;proper installation instructions&lt;&#x2F;a&gt; for your OS.&lt;&#x2F;p&gt;
&lt;p&gt;For Debian, as long as you have root on the system, that&#x27;s as follows:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; curl --proto &amp;#39;=https&amp;#39; --tlsv1.2 -L https:&#x2F;&#x2F;nixos.org&#x2F;nix&#x2F;install&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; sh&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; -s -- --daemon&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; nix --version&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;nix&lt;&#x2F;span&gt;&lt;span&gt; (Nix) ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; mkdir -p ~&#x2F;.config&#x2F;nix&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; echo &amp;quot;experimental-features = nix-command flakes&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; &amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; ~&#x2F;.config&#x2F;nix&#x2F;nix.conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;a-gentle-introduction-to-flakes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-gentle-introduction-to-flakes&quot; aria-label=&quot;Anchor link for: a-gentle-introduction-to-flakes&quot;&gt;A gentle introduction to Flakes&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;You may have noticed that we enabled two experiments in the &lt;code&gt;nix.conf&lt;&#x2F;code&gt; file above. Although flakes are officially still experimental, they have been widely adopted by the Nix community and are often the recommended way of managing a Nix project.&lt;&#x2F;p&gt;
&lt;p&gt;To start, create a new directory for your project and create a &lt;code&gt;flake.nix&lt;&#x2F;code&gt; file inside.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;  description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;My first reproducible devshell!&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;  inputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;github:NixOS&#x2F;nixpkgs&#x2F;nixos-unstable&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt; 7&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;  outputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; nixpkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; }: {};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt; 9&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A Flake is like Nix&#x27;s manifest file. It describes a list of input flakes (things like &lt;code&gt;nixpkgs&lt;&#x2F;code&gt;, the Nix package repository, or other Nix dependencies) and a list of outputs it produces. This is all, of course, written in the Nix programming language!&lt;&#x2F;p&gt;
&lt;p&gt;In our case, we bring in &lt;code&gt;NixOS&#x2F;nixpkgs&lt;&#x2F;code&gt;, specifically the &lt;code&gt;unstable&lt;&#x2F;code&gt; channel which is Nix&#x27;s rolling-release branch.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;defining-a-devshell&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#defining-a-devshell&quot; aria-label=&quot;Anchor link for: defining-a-devshell&quot;&gt;Defining a &lt;code&gt;devShell&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Inside of &lt;code&gt;outputs&lt;&#x2F;code&gt;, we&#x27;ll define a single development shell, for the &lt;code&gt;x86_64-linux&lt;&#x2F;code&gt; system.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;  outputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; nixpkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #EBA0AC;font-style: italic;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; }: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt; 9&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;    devShells&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;x86_64-linux&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;      let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt; pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; nixpkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt; system&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; &amp;quot;x86_64-linux&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;11&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;      in&lt;&#x2F;span&gt;&lt;span&gt; pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mkShell&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;        buildInputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; with&lt;&#x2F;span&gt;&lt;span&gt; pkgs;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; [];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;13&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;      };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A few notable lines here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;devShells.x86_64-linux.default&lt;&#x2F;code&gt;: defines the specific output we&#x27;re declaring. In many larger projects, these are automatically generated for all supported system types, but this&#x27;ll do for now.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;let pkgs = import nixpkgs { ... }; in&lt;&#x2F;code&gt;: nixpkgs in particular is not built for flakes yet, so it is &quot;imported&quot; in this way. We also need to tell nixpkgs our &lt;code&gt;system&lt;&#x2F;code&gt;, which is our architecture and OS.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pkgs.mkShell&lt;&#x2F;code&gt;: creates a development shell.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;buildInputs = with pkgs; [];&lt;&#x2F;code&gt;: declares the packages we want available in our devShell, which for now is nothing.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;bringing-in-llvm-22&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#bringing-in-llvm-22&quot; aria-label=&quot;Anchor link for: bringing-in-llvm-22&quot;&gt;Bringing in LLVM 22&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Now, we tell Nix to add the packages for LLVM 22 we want: let&#x27;s say &lt;code&gt;llvm&lt;&#x2F;code&gt;, &lt;code&gt;libllvm&lt;&#x2F;code&gt;, &lt;code&gt;clang&lt;&#x2F;code&gt;, and &lt;code&gt;lld&lt;&#x2F;code&gt; (as these are what we use with Zirco).&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt;      in&lt;&#x2F;span&gt;&lt;span&gt; pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mkShell&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;13&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89B4FA;&quot;&gt;        buildInputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CBA6F7;&quot;&gt; with&lt;&#x2F;span&gt;&lt;span&gt; pkgs;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;14&lt;&#x2F;span&gt;&lt;span&gt;          llvmPackages_22&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;llvm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;          llvmPackages_22&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;libllvm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;16&lt;&#x2F;span&gt;&lt;span&gt;          llvmPackages_22&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;clang&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;17&lt;&#x2F;span&gt;&lt;span&gt;          llvmPackages_22&lt;&#x2F;span&gt;&lt;span style=&quot;color: #94E2D5;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;lld&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot; style=&quot;background-color: #CDD6F412;&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;18&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;        ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;giallo-ln&quot; style=&quot;color: #7F849C;&quot;&gt;19&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9399B2;&quot;&gt;      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, we can enter our devShell:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #CDD6F4; background-color: #1E1E2E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; clang --version&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;clang:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; Command not found&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; nix develop&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; clang --version&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89B4FA;font-style: italic;&quot;&gt;clang&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FAB387;&quot;&gt; 22.0.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E3A1;&quot;&gt; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Nix has now grabbed the packages we needed and stored them in &lt;code&gt;&#x2F;nix&lt;&#x2F;code&gt;, and temporarily set up the environment for us (e.g. &lt;code&gt;$PATH&lt;&#x2F;code&gt;) so that we can use this toolchain. Notice that none of this touches your system - no package is installed into &lt;code&gt;&#x2F;usr&lt;&#x2F;code&gt;. When we exit the shell, our environment is back to normal — no more fighting with the system package manager, and you haven&#x27;t tainted your system!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-real-benefits&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-real-benefits&quot; aria-label=&quot;Anchor link for: the-real-benefits&quot;&gt;The real benefits&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Cool, we have a working development environment. But there&#x27;s a lot more to Nix than just that.&lt;&#x2F;p&gt;
&lt;p&gt;Nix also lets you:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Define your &quot;dotfiles&quot; and other system-wide configuration in a reproducible way (using &lt;code&gt;home-manager&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;Build and deploy software that works no matter where it is built&lt;&#x2F;li&gt;
&lt;li&gt;Declare global service configurations and manage your entire infrastructure with no Ansible&lt;&#x2F;li&gt;
&lt;li&gt;Much, much more!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;I installed Nix because I needed LLVM 22 on Debian, but I stayed because of how it empowered me to write software and manage my systems in a way where they work almost everywhere. Debian still remains my system of choice, but Nix has become a tool I use daily.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s much more to it: I haven&#x27;t talked about Home Manager, packaging for Nix, and more, but we&#x27;ll get to that in future posts.&lt;&#x2F;p&gt;
&lt;p&gt;In the meantime, if you&#x27;re new to Nix, you should give &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;guides&#x2F;nix-pills&#x2F;&quot;&gt;Nix Pills&lt;&#x2F;a&gt; a read, and if you&#x27;re a seasoned Nix&#x2F;NixOS user, maybe try it out on top of your distro of choice. You&#x27;d might be surprised at what that balance can do.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
