1 pontos por GN⁺ 2025-06-09 | 1 comentários | Compartilhar no WhatsApp
  • A Railway redesenhou seu builder, que cria imagens de contêiner a partir do código dos usuários, levando para o Railpack a experiência do Nixpacks, que já construiu mais de 14 milhões de apps
  • O Nixpacks era suficiente para 80% dos usuários, mas os outros 200 mil usuários da Railway podiam esbarrar em limitações de gerenciamento de versões, tamanho de imagem e cache
  • O Railpack aumenta a reprodutibilidade dos builds com versões major.minor.patch, travamento de dependências e um fluxo de instalação baseado no Mise, em vez do gerenciamento de versões do Nix baseado em commits
  • Ao gerar diretamente BuildKit LLB e Frontend, a imagem Node padrão ficou 38% menor e a imagem Python padrão ficou 77% menor, além de permitir cache compartilhável entre ambientes
  • O Railpack está atualmente em Beta e pode ser ativado nas configurações do serviço; a Railway está priorizando elevar a maturidade das linguagens mais usadas antes de ampliar o suporte a muitas linguagens

Contexto da criação do novo builder da Railway

  • A Railway apresentou o Railpack como a próxima etapa do builder da Railway
  • O Railpack foi desenvolvido do zero com base na experiência adquirida ao construir mais de 14 milhões de apps com o Nixpacks
  • O Nixpacks foi lançado há cerca de 3 anos e se tornou o método padrão na Railway para criar imagens a partir do código dos usuários
  • Ele funcionava bem para 80% dos usuários, mas os outros 200 mil usuários da Railway podiam enfrentar limitações
  • A Railway concluiu que precisaria fazer um grande upgrade no builder para aumentar sua base de usuários de 1 milhão para 100 milhões

Limitações que o Nixpacks encontrou com o Nix

  • O maior problema era o gerenciamento de versões de pacotes baseado em commits do Nix
    • Cada pacote oferece apenas a versão major mais recente
    • A versão fica vinculada a um commit específico do repositório nixpkgs
  • A tentativa de oferecer suporte a todas as versões de patch acabou virando uma estrutura que mapeava manualmente strings de versão para SHAs de commit, algo que não era claro nem fácil de manter para contribuidores não familiarizados com o gerenciamento de versões do Nix
  • Linguagens como Node e Python acabaram ficando com suporte apenas à versão major mais recente
  • Ao atualizar um SHA de commit para oferecer suporte a uma versão de pacote mais recente, outras versões de pacotes também podiam mudar junto
    • Quando a versão padrão mudava, aumentava a chance de builds de usuários que antes funcionavam falharem com erros inesperados
    • Para a Railway, um build que antes passava quebrar de repente era pior do que usuários não terem acesso aos pacotes mais recentes

Problemas de tamanho de imagem e cache

  • A forma como o Nixpacks trazia dependências via Nix muitas vezes gerava imagens grandes
  • O Nix e os pacotes e bibliotecas relacionados necessários em build e runtime ficavam em uma única camada /nix/store
  • Como não havia como dividir as dependências do Nix em camadas separadas, havia um limite para reduzir o tamanho da imagem final
    • A Railway vê isso não como um problema do Nix em si, mas como um problema da forma como o Nixpacks usa o Nix
  • No cache, também era difícil controlar quando o cache de camadas seria invalidado
  • A Railway injeta uma variável de ambiente com o ID de deploy em todos os builds
    • Em um Dockerfile, as camadas executadas depois que essa variável é adicionada sempre eram invalidadas e não podiam ser armazenadas em cache
  • A abordagem de esconder dos usuários elementos centrais do Nix também não se encaixava bem
    • A intenção era que os usuários não precisassem entender o que é uma derivation nem por que o Node 22.14.0 está em uma versão específica de archive do canal unstable

Mudanças estruturais do Railpack

  • A Railway criou o Railpack para resolver os problemas encontrados no Nixpacks
  • Ao se afastar do Nix, o nome também mudou de Nixpacks para Railpack
  • A base de código mudou de Rust para Go por causa das bibliotecas do BuildKit
  • O Railpack controla de forma mais direta como a imagem final é criada
    • Ele gera diretamente BuildKit LLB e Frontend
    • Em comparação com o Nixpacks, a imagem Node padrão ficou 38% menor, e a imagem Python padrão ficou 77% menor
    • Ele usa o Mise para resolução de versões e a maior parte das instalações de pacotes
    • Também deixa espaço para oferecer suporte a outras fontes de executáveis no futuro
  • É possível travar as dependências usadas em um build bem-sucedido
    • Isso pode impedir que o build quebre mesmo se a versão padrão do Node mudar de 22 para 24
  • Ele usa BuildKit secrets para impedir que variáveis de ambiente secretas apareçam nos logs de build ou na imagem final

Como um build do Railpack funciona

  • O processo do Railpack é dividido em três etapas
    • Analyze: examina o código e decide quais pacotes instalar, quais comandos executar e qual comando de inicialização usar
    • Plan: cria um plano de build serializável em JSON, composto por várias etapas; cada etapa obtém entradas de outras etapas ou da imagem inteira
    • Generate: monta o grafo de build do BuildKit de acordo com as entradas e saídas do plano
  • Um Dockerfile é linear, mas o grafo do BuildKit é muito mais paralelo
  • Cada comando é executado em sua própria etapa dentro de um build multiestágio, permitindo controle fino sobre as camadas de entrada e sobre como o sistema de arquivos final é montado
  • O Railpack gera um plano de build contendo todas as etapas de build necessárias
    • Cada etapa define explicitamente as etapas anteriores ou imagens de que precisa
    • Esse formato é de nível mais baixo do que o usado no Nixpacks
    • O plano é convertido em um grafo no formato LLB e interpretado
  • O BuildKit começa pelo fim e trabalha de trás para frente, buscando no cache quando possível e executando comandos apenas quando necessário para resolver a camada solicitada
  • Para invalidar camadas quando variáveis de ambiente específicas mudam, o Railpack gera um hash dos valores das variáveis usadas e monta no sistema de arquivos de entrada um arquivo que contém esse hash
    • Se o código e as variáveis usadas não mudarem, há acerto no cache da camada
  • O Railpack consegue definir completamente como a imagem é criada

O que se tornou possível com o Railpack

  • É possível construir e publicar sites estáticos em Vite, Astro, CRA e Angular sem configuração
  • A integração entre o build e a UI da Railway fica mais estreita
  • É possível oferecer suporte às versões mais recentes das linguagens sem um release do Railpack
  • É possível usar cache de camadas otimizado entre vários ambientes de um projeto

Como usar atualmente e escopo de suporte

  • O Railpack está atualmente disponível em Beta e pode ser ativado nas configurações do serviço
  • Ele já está sendo usado nos builds de railway.com e da central station
  • O suporte atual inclui:
    • Node
    • Python
    • Go
    • PHP
    • Deploy de HTML estático
    • Suporte básico a sites estáticos em Vite, Astro, CRA e Angular
  • O objetivo da Railway é oferecer um ambiente em que seja fácil publicar tanto frontend quanto backend
  • O suporte a frameworks e linguagens continua sendo adicionado
    • Pedidos podem ser enviados pela Help Station
    • Até que as APIs centrais e abstrações estejam definidas, a prioridade é a profundidade nas linguagens mais usadas, e não a amplitude de suporte
  • O Railpack é open source, e a documentação está disponível em railpack.com

1 comentários

 
GN⁺ 2025-06-09
Opiniões do Hacker News
  • Sou entusiasta do Nix, mas não pretendo criticar a Railway por se afastar dele. Ainda assim, algumas reclamações parecem precisar de mais explicação.
    Nixpkgs é excelente, mas não é a mesma coisa que Nix; e, se a ideia é obter versões arbitrárias de toolchains, o Nixpkgs simplesmente não é o ideal. As ferramentas Nix para trazer versões arbitrárias de Rust já são muito boas, e outras ferramentas de desenvolvimento baseadas em Nix também mostraram formas de lidar bem com isso.
    Também não entendo a afirmação de que “não há como separar dependências Nix em uma camada à parte”. Dá para separar simplesmente da forma que você quiser, e as ferramentas Docker embutidas do Nixpkgs também têm algum suporte a isso.
    A mudança de Rust para Go não tem relação direta com Nix, mas é interessante, e também soa como se Railpacks e Nixpacks tivessem sido feitos por pessoas diferentes. Já vi coisas bem ruins acontecerem quando pessoas não familiarizadas com Nix acabam tendo de assumir uma solução Nix inacabada dentro de uma organização; por isso, no trabalho, normalmente evito criar esse tipo de situação.

    • Não uso Nix, mas a reação de que “Nixpkgs não é Nix” soa um pouco dismissiva. Se Nixpkgs é o padrão e alternativas exigem pesquisa e esforço adicionais, então, para a maioria dos usuários, na prática isso é Nix.
      A frase “basta dividir as camadas do jeito que quiser” também depende de isso ser claro, simples e o comportamento padrão.
    • É cansativo ver, sempre que surge um problema básico de usabilidade no Nix, a resposta recorrente de que “existe uma solução alternativa”. Essas soluções exigem conhecimento transmitido informalmente, obrigam a escrever dezenas ou centenas de linhas em uma linguagem que funciona de forma totalmente diferente das linguagens mainstream, e ainda têm mensagens de erro e documentação da biblioteca padrão ruins.
      As pessoas se frustram com Nix não porque ele não seja Turing-completo, mas porque ele não oferece uma API de primeira classe simples que se encaixe diretamente nos projetos idiomáticos daquele ecossistema; com isso, acaba criando mais problemas do que resolve.
      Se todo projeto que tenta usar Nix acaba caminhando para escrever seus próprios módulos para corrigir problemas do Nix, há pouco motivo para escolher Nix em vez de ferramentas mainstream com boa documentação. Este caso parece exatamente isso, e acho que a maioria simplesmente escolheria Docker.
      É frustrante ver um produto voltado a desenvolvedores se prender a flakes ideologicamente puros, em vez de resolver problemas práticos de experiência de desenvolvimento em uma escala de tempo que não seja geológica. Sei que se trata de contribuição voluntária, mas é muito triste ver tanto esforço técnico indo para algo que, por causa da má experiência de usuário, acaba sendo praticamente difícil de usar.
    • Um ponto fácil de perder é que os usuários da Railway são desenvolvedores que querem especificar suas próprias dependências e versões para pacotes arbitrários.
      Na forma como o Nix funciona junto com a estrutura do Nixpkgs, fixar uma versão de pacote significa fixar o commit de toda a árvore do nixpkgs. Builds de pacotes node/python/ruby também dependem do estado da árvore fora do diretório do pacote, então passa a ser necessário mapear versões para commits.
      Como essa abstração vaza, a Railway precisa expô-la aos usuários; e o usuário, que só queria fazer yarn add new-fancy-nodejs-package-with-linked–native-deps, pode acabar tendo de conciliar vários estados do repositório nixpkgs.
      Para usos de escopo estreito, talvez seja aceitável usar Nix sem Nixpkgs, mas em uma plataforma como a Railway isso parece difícil de justificar.
    • Ouvi esse tipo de frase, “Nix != Nixpkgs”, também em relação aos ports do FreeBSD quando experimentei FreeBSD. O pkg funcionava em geral bem, mas um dia tentei compilar o vim a partir dos ports com uma flag USE personalizada; ele puxou mais de 20 dependências, perguntou opções em cada make menuconfig e, então, o 16º pacote de 23 falhou com algo do tipo “isto precisa daquilo, aquilo precisa de Fubar3.32.1, mas Fubar3 foi deprecated em favor de Fubar4”, e eu desisti.
      Entendo que os desenvolvedores do Core OS não conseguem dar suporte a mais de 10 mil pacotes, mas também é preciso deixar claro que, na prática, quando você tenta ativar recursos personalizados, a chance de falha é alta. Ou então seria melhor estabelecer que uma build padrão gerada independentemente precisa funcionar antes de aparecer nos ports, e remover da lista de ports aquilo que não compila.
    • Acho que isso foi bem colocado. Eu acrescentaria que, embora nixpkgs não seja o Nix em si, nixpkgs também é a parte boa. Usando NixOS, pela primeira vez estou usando a versão mais recente do kernel Linux no dia do lançamento, e isso é bem ótimo. Com a idade, passei a aceitar também o Debian Stable, mas sempre há a sensação de voltar alguns anos no tempo.
      Dá para criticar a linguagem Nix por horas, mas ela é antiga, foi o melhor que se pôde fazer na época e talvez hoje não valha muito a pena mudá-la. O sistema de build do Nix parece bastante primitivo e frequentemente recompila até coisas que não parecem precisar ser recompiladas. Por exemplo, uma parte considerável da build da ISO de instalação do NixOS depende da linha de comando passada ao kernel, console=ttyS2,1500000n8, então só mudar a velocidade da porta serial exige cerca de 3 minutos de build. É engraçado, mas não vou deixar de usar Nix por causa disso; apenas não aceitaria isso nas minhas próprias builds.
      Para imagens Docker, pessoalmente acho que é onde o Nix se sai pior. Há muito tempo, ao fazer software em Go, precisei adicionar o binário pg_dump do Postgres a uma imagem de contêiner. Usei Nix como o time de infraestrutura sugeriu, e a imagem com um binário Go comprimido de 50 MB virou 1,5 GB de origem misteriosa. O pg_dump tem 464 KB. No fim, usei Bazel com rules_debian para instalar pacotes apt, e em cima de distroless ficou muito mais limpo e menor. Pela minha experiência real com Nix, os sistemas Nix parecem sempre acabar com 1,4 GB. A ISO de instalação também tem 1,4 GB; uma máquina recém-instalada também tem 1,4 GB.
      A situação de querer compilar um grande projeto C++ já é um caminho bem pavimentado, e trocar C++ por Rust não muda a essência. Existem sistemas de build que tornam a situação das bibliotecas menos dolorosa; eles também são tão complexos quanto Nix, mas alguns se encaixam melhor nesse uso. Como o Nix tenta ser um sistema de build para software dos outros e para o nixpkgs, ele acaba ficando em um ponto muito genérico. Sistemas de build projetados para compilar o seu próprio software normalmente fazem melhor esse trabalho. Pessoalmente, estou satisfeito com Bazel e, fora go build para projetos só em Go, acho que quase não usaria outra coisa, mas há muitas opções. Em 99% dos casos, use algo assim em vez de Nix, e escreva um flake para que as pessoas possam instalar a versão mais recente com home-manager.
  • A parte sobre escolha de versões soa estranha. A versão do nixpkgs faz sentido quando se executa ou compila um sistema, mas, se a plataforma oferece um runtime ou compilador, é preciso um modelo como o do devenv, em que você fornece as versões diretamente
    Se você acaba compilando um sistema antigo só para oferecer um nodejs antigo, deixa de receber patches de segurança das dependências. O Devenv lida com isso, por exemplo, via https://github.com/cachix/nixpkgs-python, no estilo “manter todas as versões do Python atualizadas de hora em hora com Nix”
    O fato de a Railway injetar uma variável de ambiente com o ID de deploy em todos os builds poderia ter sido feito na camada seguinte à instalação. Também dá para dividir pacotes em várias camadas, e há automação por agrupamentos para reduzir o número de camadas

  • “Não havia problema com o Nix em si; havia problema com a forma como o usávamos” é um bom exemplo de usar a ferramenta certa para o trabalho certo. O Nix é excelente para alguns usos e terrível para outros
    O problema é que a curva de aprendizado do Nix é tão alta que, quando você entende o suficiente para avaliar, já investiu tempo demais para sentir que vale a pena voltar atrás, e acaba forçando a ferramenta para tentar resolver a necessidade original

    • Eu sinto algo parecido, mas, em certos aspectos, o Nix está mais próximo de um paradigma de programação genérico do que outros sistemas operacionais. Só não estamos acostumados a pensar em sistemas operacionais dessa forma. Expressões Nix têm entradas, incluem repositórios de pacotes e muitos pares chave-valor, e a saída é um sistema Linux. Daqui a alguns anos, isso pode parecer muito mais normal
      Por causa desse paradigma, é muito fácil para uma IA criar um shell.nix ou configuration.nix que atenda a uma especificação. Por exemplo, pode conter pacotes Python, pacotes Linux, variáveis de ambiente, entradas de caminho etc.
      Uso isso com frequência para incluir no repositório um ambiente que ofereça suporte completo ao pacote em questão. Com flakes seria mais reprodutível, mas entendo flake.nix como algo parecido com um shell.nix com versões fixadas, e ainda estou aprendendo
  • Parece uma tentativa de enfiar versões onde não há versões. É como tentar colocar um cubo quadrado em um buraco redondo
    Dizem que a “versão padrão” quebra coisas que dependem dela; não entendo o que isso quer dizer. É como usar a tag :latest do Docker e ficar surpreso quando um novo servidor sobe com uma versão diferente da imagem “padrão” anterior e tudo quebra
    Não entendo nenhuma das explicações desse post. Parecem pessoas que não têm ideia do que é uma “versão” de software
    Também não entendo por que dizem que “não há como separar dependências Nix em uma camada separada”. É claro que dá para dividir /nix/store em quantas camadas forem necessárias. Fico até em dúvida se eles sabem como usar contêineres e Nix em primeiro lugar
    Dada essa imaturidade evidente, não surpreende que a solução proposta cheire a peixe podre. É a clássica síndrome do NIH, e me parece bem provável que os mesmos problemas que eles não conseguiram resolver com Nix se espalhem para a nova “solução”

    • Sou a favor de não usar Nix, especialmente onde isso não faz sentido. Mas, quando bastariam algumas horas para ver como as pessoas já resolvem esses problemas, recriar do zero um sistema que funciona por motivos que na verdade nem são problemas parece fundamentalmente estranho
      Como outros disseram, nix2container e flakes parecem resolver todos os problemas que eles tinham
      Quanto a versionamento, flakes que escrevi há 3 anos ainda compilam hoje exatamente com as mesmas versões e a mesma saída de quando foram escritos
      Embora isso soe como uma tentativa de ir ao mercado como plataforma e levantar investimento
      Edição: acabei de verificar o GitHub do nixpacks e algo saltou aos olhos: eles usam rustPlatform do nixpkgs, e não o rust-overlay[0] da oxalica, que teria aparecido com uma busca superficial por problemas de Rust. rust-overlay é um dos overlays mais úteis e poderosos que já usei
      [0] https://github.com/oxalica/rust-overlay
    • Se o objetivo é conseguir investimento de VC, uma plataforma de deploy vende melhor do que um wrapper de Nix
  • nix2container[1] de fato permite separar dependências em camadas próprias. Você pode criar explicitamente uma camada com apenas parte das dependências necessárias para a imagem, e há exemplos nesta seção: https://github.com/nlewo/nix2container?tab=readme-ov-file#is...
    Por exemplo, se as imagens usam bash, você pode criar explicitamente uma camada contendo o closure do bash. Essa camada é reutilizada por todas as imagens e só é recompilada e reenviada quando aquele closure do bash muda
    A ideia de que a imagem fica grande por causa de uma única camada /nix/store se aplica à função padrão nixpkgs.dockerTools.buildImage, mas não ao nix2container nem ao nixpkgs.dockerTools.streamLayeredImage. Essas ferramentas, em vez de escrever as camadas na Nix store, criam scripts que usam caminhos existentes da store para realmente enviar a imagem. A implementação do nix2container cria um arquivo JSON que descreve os caminhos da Nix store de todas as camadas, e o Skopeo consome esse JSON para enviar a imagem ao daemon do Docker, a um registry, ao podman etc.
    Para referência, eu sou o autor do nix2container
    [1] https://github.com/nlewo/nix2container

    • Uso em deploys na AWS ECR graças ao nix2container, e o tempo de iteração entre builds caiu para segundos de um dígito
    • Eu estava reservando um tempo para experimentar o nix2container por causa de problemas com o tamanho das imagens Docker. Obrigado pelo trabalho
  • O problema central aqui é se apegar à atitude de sopa de versões personalizada incentivada por gerenciadores de pacotes de linguagens. Esse modelo é totalmente insustentável
    A alternativa, Mise, não parece ter capacidade de entender restrições de versão entre pacotes, nem parece executar testes para verificar se cada pacote instalado funciona bem com as versões ao redor. Então você não está obtendo a mesma coisa de forma alguma

  • Uma “sopa” de versões personalizadas é insustentável, mas um dos motivos pelos quais as pessoas continuam usando isso é que, em geral, funciona bem. Um dos motivos de funcionar bem é que as bibliotecas no nível do sistema operacional vêm de um outro mundo, muito mais conservador, e tentam evitar ao máximo quebrar compatibilidade retroativa
    Por isso dá para usar como base um sistema operacional estável e bem mantido, empilhar por cima uma sopa de versões personalizadas de ferramentas e runtimes de linguagens com ferramentas como mise ou asdf, e então executar o app. Quase nunca quebra. Quando quebra, você mexe em versões e pequenos ajustes até voltar a funcionar e segue em frente. O fato de ter quebrado é irritante, mas não é importante. Aumentar o atrito, exigir aprendizado ou demandar mais trabalho é perda de tempo
    Por outro lado, também há pessoas que procuram uma solução para impedir que isso volte a quebrar. Para elas, o problema é importante, então não há problema se a solução exigir atrito, aprendizado e trabalho extra. São essas pessoas que querem Nix
    A maioria pertence ao primeiro grupo, então uma empresa como a Railway, que quer crescer, acaba escolhendo uma solução adequada a esse grupo

    • Você pode explicar melhor o que significa essa postura de “sopa de versões personalizadas” e qual seria a alternativa?
    • Se feito corretamente, dá para ter os dois. Por exemplo, é trivial compilar pacotes Rust com Nix a partir de um arquivo Cargo.lock. O nixpkgs vai na direção oposta da sopa de versões personalizadas, mas o próprio Nix consegue lidar bem com esse modelo também
  • Pela minha experiência trabalhando com DevOps/SRE, quando alguém tenta criar um sistema para gerenciar dependências etc., em geral acaba seguindo um de dois caminhos. Dá para usar Python como exemplo
    Opção 1: “vamos usar um grande repositório único e compartilhado.” A vantagem é que tudo fica em um só lugar, o que você precisa está incluído, e todo mundo usa a mesma coisa, então é mais fácil corrigir problemas como vulnerabilidades. A desvantagem é que sempre alguém quer uma versão especial, deploys graduais são difíceis e mudanças tendem a virar big bang, além de surgir a pergunta “como fazemos uma versão Docker pequena?”
    Opção 2: “cada um terá seu próprio conda/venv.” A vantagem é que cada pessoa obtém exatamente o que quer, não usa pacotes desnecessários, e upgrades graduais são fáceis. A desvantagem é que vira “quantos ambientes conda existem, afinal?”, bibliotecas de grupos diferentes podem não ser testadas com a mesma combinação de bibliotecas Python, e gerenciar vulnerabilidades vira um pesadelo porque nem se sabe onde estão os diferentes ambientes conda
    Por isso sempre sou cético quando alguém diz “essa nova abordagem resolve tudo”. Quanto mais tempo de carreira passa, mais verdadeira fica a frase “não existem soluções, só trade-offs

  • Mesmo tendo pouca experiência com Nix, os pontos aqui não me parecem muito corretos
    Há afirmações como “não é claro nem sustentável para contribuidores que não estão familiarizados com o versionamento do Nix” e “Node e Python passaram a dar suporte apenas às versões major mais recentes”, mas não entendo por que isso seria impossível de manter. Se o problema é ter que montar uma lista de versões disponíveis, fico me perguntando se isso não poderia ser automatizado
    Além disso, não entendo por que a Railway define a forma como os usuários usam Nix. Um dos pontos centrais do Nix não é justamente poder configurar uma máquina vazia com as versões exatas dos pacotes que você quer? Não entendo por que a Railway precisa se colocar entre o usuário e essas versões para impor restrições
    Se a arquitetura for tal que o usuário não veja Nix diretamente, a pergunta original ainda permanece. Não dá para automatizar a lista de versões de pacotes?

    • A limitação de versões vem do fato de que o cache do Nix não mantém versões antigas. Então, se você usar uma versão antiga, precisa compilar a partir do código-fonte. Pelo texto, parece que eles não queriam fornecer o próprio cache para versões antigas, mas isso não parece exigir tanto esforço assim
      Sinceramente, os motivos apresentados não parecem muito sólidos. Talvez a pessoa que introduziu Nix tenha saído, e quem ficou não gostasse muito dele. A linguagem em si não é lá muito boa, e a documentação antiga também não era excelente
      Ainda assim, embora eu não conheça bem o stack que eles escolheram, fico curioso se ele oferece um nível de determinismo próximo ao do Nix. Caso contrário, isso pode atrapalhá-los no futuro ou tornar a operação mais difícil
    • O texto diz que “a forma como o Nixpacks traz dependências resulta em uma imagem enorme com uma única camada /nix/store, onde ficam todos os pacotes e bibliotecas relacionados ao Nix necessários para build e runtime”
      Isso é parecido com dizer “desistimos de carros porque não conseguimos fazer um carro andar para a frente”. Essa é uma das coisas que o Nix faz de forma mais confiável. Ele detecta automaticamente as dependências de runtime que são de fato referenciadas pelo binário resultante por correspondência de strings de hash em /nix/store
      Se eles não conseguiram fazer isso, então estavam usando de um jeito bem estranho ou fizeram algo gravemente errado. É até difícil pensar em uma forma de impedir que o Nix resolva isso automaticamente
      Por isso eu não levaria tão a fundo a experiência deles com Nix. O conteúdo sobre versionamento é um problema muito comum que todo mundo enfrenta, então teria sido mais interessante se eles tivessem tentado resolvê-lo
    • A interpretação de que a estrutura impede o usuário de ver Nix diretamente é pelo menos a mesma que a minha
    • Mesmo que o projeto não tenha nenhum Dockerfile, se você fizer push do código para a Railway, ela cria a imagem com Nixpacks. Os logs de build mostram coisas relacionadas ao Nix, mas a maior parte roda nos bastidores
  • Nix não oferece garantia de versão arbitrária, e sim garantia de commit. Quando surgirem casos de borda, eles vão sofrer com mudanças na glibc ou bibliotecas compartilhadas conflitantes
    Parece um pouco tarde, mas eu ficaria feliz em oferecer consultoria para fazer isso funcionar do jeito idiomático do Nix. O produto parece legal

    • O Nix resolve problemas de incompatibilidade de bibliotecas compartilhadas de forma extremamente conservadora. Se qualquer coisa mudar — seja uma mudança significativa ou não, até uma correção de comentário, uma alteração de documentação ou a adição de um caso de teste — ele recompila todas as dependências
      E não só isso: também as dependências das dependências, e as dependências dessas dependências, continuando assim, o que frequentemente causa rebuilds em grande escala
      Ele evita conflitos de bibliotecas compartilhadas, mas essa solução é extremamente desperdiçadora e também pode tornar o desenvolvimento doloroso. Dá para ver isso no processo de staging do nixpkgs
    • Eu entendo completamente a proposta de valor do Nix. Mas acho que a expressão “vão sofrer” é meio exagerada. No máximo, eu diria algo como “perdem garantias bastante importantes em comparação com o Nix”
      Ainda assim, provavelmente ficará em um estado “empacotado com alta chance de funcionar corretamente” em comparação com 95% do software do mundo
  • Não entendo por que não foi possível criar derivations próprias em vez de depender do hash do nixpkgs