water

water

Are there any known working resources to package using nix and deploy it?

I’ve tried:

But the basic liveview(mix phx.new) uses heroicons and I get errors when building it.

nix build

...
       > Running phase: installPhase
       > Unchecked dependencies for environment prod:
       > * heroicons (https://github.com/tailwindlabs/heroicons.git - v2.1.1)
       >   lock mismatch: the dependency is out of date. To fetch locked version run "mix deps.get"
       > ** (Mix) Can't continue due to errors on dependencies
...

Showing Posts 1 to 10

munksgaard

munksgaard

I’ve found the best resource to be the BEAM section of the nixpkgs manual. We’re currently building and deploying our platform using nix, and I used that guide as a starting point for the implementation.

I’ve been meaning to write a proper blog post about this at some point, but haven’t gotten around to it yet.

Do you have any specific questions you’d like help with?

Edit: You did actually pose a question, so let me try to help out:

But the basic liveview(mix phx.new) uses heroicons and I get errors when building it.

What does your mix.exs and flake.nix look like?

Zurga

Zurga

Maybe try to doing aMIX_ENV=prod mix deps.get. If there is a lock mismatch, this could solve it.

I am not a big fan of just throwing your working directory in as source. Any changes made to files that are not related would result in a rebuild of everything, and all files in directory that are not related to a deployment would also be included. I suggest using the documentation provided and mix2nix for dependencies.

water

water OP

error after running node2nix:

node:fs:562
  return binding.open(
                 ^

Error: ENOENT: no such file or directory, open 'package.json'
    at Object.openSync (node:fs:562:18)
    at Object.readFileSync (node:fs:446:35)
    at Object.npmToNix (/nix/store/w2s44ll9wl5k8ncdqyrbgvmyd1dj6p6w-node2nix-1.11.0/lib/node_modules/node2nix/lib/node2nix.js:46:29)
    at Object.<anonymous> (/nix/store/w2s44ll9wl5k8ncdqyrbgvmyd1dj6p6w-node2nix-1.11.0/lib/node_modules/node2nix/bin/node2nix.js:296:10)
    at Module._compile (node:internal/modules/cjs/loader:1554:14)
    at Object..js (node:internal/modules/cjs/loader:1706:10)
    at Module.load (node:internal/modules/cjs/loader:1289:32)
    at Function._load (node:internal/modules/cjs/loader:1108:12)
    at TracingChannel.traceSync (node:diagnostics_channel:322:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:220:24) {
  errno: -2,
  code: 'ENOENT',
  syscall: 'open',
  path: 'package.json'
}

The error is correct, assets/package.json doesn’t exist
Are there any commands I (or nix) should be running?

> tree assets
assets
├── css
│   └── app.css
├── js
│   └── app.js
├── tailwind.config.js
└── vendor
    └── topbar.js

These commands I’ve used

echo "use flake" > .envrc
echo ".direnv/" >> .gitignore


mix archive.install hex phx_new

mix phx.new . --module Proj --app proj

# Modify dev.exs by adding the db's port below the hostname:
sed -i '/hostname: "/a \ \ port: 5433,' config/dev.exs

# START POSTGREQL
pg-start

# 
mix deps.get

# Then configure your database in config/dev.exs and run:
mix ecto.create

# You can also run your app inside IEx (Interactive Elixir) as:
iex -S mix phx.server


cd assets
node2nix --development

flake.nix

{
  description = "General Elixir Project Flake 20250516";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs =
    {
      self,
      nixpkgs,
      flake-utils,
    }:
    flake-utils.lib.eachDefaultSystem (
      system:
      let
        pname = "proj";
        version = "0.0.1";

        pkgs = nixpkgs.legacyPackages.${system};
        ## Define Erlang with ODBC support
        erlang = pkgs.beam.interpreters.erlang_27.override {
          odbcSupport = true;
        };
        ## Create a custom beam package set using our ODBC-enabled Erlang
        erlangPackages = pkgs.beam.packagesWith erlang;
        ## Now we can create Elixir using our custom beam packages
        elixir = erlangPackages.elixir_1_18;

        nodejs = pkgs.nodejs_24;
        postgresql = pkgs.postgresql_17;

        # LANG = "C.UTF-8";
        LANG = "en_US.UTF-8";
        root = ./.;
      in
      {
        formatter = pkgs.nixpkgs-fmt;

        ## mix2nix is a cli tool available in nixpkgs. it will generate a nix expression from a mix.lock file. It is quite standard in the 2nix tool series.
        # nix run nixpkgs#mix2nix -- > mix_deps.nix
        # cd assets
        # nix run nixpkgs#node2nix -- --development
        packages =
          let
            src = ./.;
            mixNixDeps = with pkgs; import ./mix_deps.nix { inherit lib beamPackages; };
            nodeDependencies = (pkgs.callPackage ./assets/default.nix { }).shell.nodeDependencies;
          in
          {
            default = erlangPackages.mixRelease {
              inherit
                src
                pname
                version
                mixNixDeps
                ;

              postBuild = ''
                ln -sf ${nodeDependencies}/lib/node_modules assets/node_modules
                npm run deploy --prefix ./assets

                # for external task you need a workaround for the no deps check flag
                # https://github.com/phoenixframework/phoenix/issues/2690
                mix do deps.loadpaths --no-deps-check, phx.digest
                mix phx.digest --no-deps-check
              '';
            };
          };

        devShells.default = pkgs.mkShell {
          inherit LANG;
          PGPORT = "5433"; # default 5432

          # enable IEx shell history
          ERL_AFLAGS = "-kernel shell_history enabled";
          # # In IEX: `open Enum.map`
          # ELIXIR_EDITOR = "code --goto __FILE__:__LINE__";

          ## phoenix related env vars
          POOL_SIZE = 15;
          # DB_URL = "postgresql://postgres:postgres@localhost:5432/db";
          PORT = 4000;
          MIX_ENV = "dev";
          ## add your project env vars here, word readable in the nix store.
          # ENV_VAR = "your_env_var";

          ##########################################################
          # Without  this, almost  everything  fails with  locale issues  when
          # using `nix-shell --pure` (at least on NixOS).
          # See
          # + https://github.com/NixOS/nix/issues/318#issuecomment-52986702
          # + http://lists.linuxfromscratch.org/pipermail/lfs-support/2004-June/023900.html
          ##########################################################
          LOCALE_ARCHIVE =
            if pkgs.stdenv.isLinux then "${pkgs.glibcLocales}/lib/locale/locale-archive" else "";

          buildInputs =
            [
              pkgs.unixODBC
              pkgs.psqlodbc

              elixir
              nodejs
              postgresql

              pkgs.git
              pkgs.pgcli
              pkgs.glibcLocales
              pkgs.gnumake
              pkgs.gcc

              ## Testing
              pkgs.chromedriver

              ## Deploy tools
              pkgs.flyctl # fly.io
              # pkgs.gigalixir

              pkgs.mix2nix
              # Used for frontend dependencies, you are free to use yarn2nix as well
              pkgs.nodePackages.node2nix
              # Formatting .js file
              pkgs.nodePackages.prettier
              pkgs.elixir_ls # Elixir

              pkgs.nixpkgs-fmt
              # codespell --skip="./deps/*,./.git/*,./assets/*,./erl_crash.dump" -w
              pkgs.codespell
              # dot -Tpng ecto_erd.dot -o erd.png
              pkgs.graphviz

              (pkgs.writeShellScriptBin "pg-stop" ''
                pg_ctl -D $PGDATA -U postgres stop
              '')
              (pkgs.writeShellScriptBin "pg-reset" ''
                rm -rf $PGDATA
              '')
              (pkgs.writeShellScriptBin "pg-setup" ''
                ####################################################################
                # If database is not initialized (i.e., $PGDATA directory does not
                # exist), then set it up. Seems superfluous given the cleanup step
                # above, but handy when one gets to force reboot the iron.
                ####################################################################
                if ! test -d $PGDATA; then
                  ######################################################
                  # Init PostgreSQL
                  ######################################################
                  pg_ctl initdb -D $PGDATA
                  #### initdb --locale=C --encoding=UTF8 --auth-local=peer --auth-host=scram-sha-256 > /dev/null || exit
                  # initdb --encoding=UTF8 --no-locale --no-instructions -U postgres
                  ######################################################
                  # PORT ALREADY IN USE
                  ######################################################
                  # If another `nix-shell` is  running with a PostgreSQL
                  # instance,  the logs  will show  complaints that  the
                  # default port 5432  is already in use.  Edit the line
                  # below with  a different  port number,  uncomment it,
                  # and try again.
                  ######################################################
                  if [[ "$PGPORT" ]]; then
                    sed -i "s|^#port.*$|port = $PGPORT|" $PGDATA/postgresql.conf
                  fi
                  echo "listen_addresses = ${"'"}${"'"}" >> $PGDATA/postgresql.conf
                  echo "unix_socket_directories = '$PGDATA'" >> $PGDATA/postgresql.conf
                  echo "CREATE USER postgres WITH PASSWORD 'postgres' CREATEDB SUPERUSER;" | postgres --single -E postgres
                fi
              '')
              (pkgs.writeShellScriptBin "pg-start" ''
                ## # Postgres Fallback using docker
                ## docker run -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres:14

                [ ! -d $PGDATA ] && pg-setup

                ####################################################################
                # Start PostgreSQL
                # ==================================================================
                # Setting all  necessary configuration  options via  `pg_ctl` (which
                # is  basically  a wrapper  around  `postgres`)  instead of  editing
                # `postgresql.conf` directly with `sed`. See docs:
                #
                # + https://www.postgresql.org/docs/current/app-pg-ctl.html
                # + https://www.postgresql.org/docs/current/app-postgres.html
                #
                # See more on the caveats at
                # https://discourse.nixos.org/t/how-to-configure-postgresql-declaratively-nixos-and-non-nixos/4063/1
                # but recapping out of paranoia:
                #
                # > use `SHOW`  commands to  check the  options because  `postgres -C`
                # > "_returns values  from postgresql.conf_" (which is  not changed by
                # > supplying  the  configuration options  on  the  command line)  and
                # > "_it does  not reflect  parameters supplied  when the  cluster was
                # > started._"
                #
                # OPTION SUMMARY
                # --------------------------------------------------------------------
                #
                #  + `unix_socket_directories`
                #
                #    > PostgreSQL  will  attempt  to create  a  pidfile  in
                #    > `/run/postgresql` by default, but it will fail as it
                #    > doesn't exist. By  changing the configuration option
                #    > below, it will get created in $PGDATA.
                #
                #   + `listen_addresses`
                #
                #     > In   tandem  with   edits   in  `pg_hba.conf`   (see
                #     > `HOST_COMMON`  below), it  configures PostgreSQL  to
                #     > allow remote connections (otherwise only `localhost`
                #     > will get  authenticated and the rest  of the traffic
                #     > discarded).
                #     >
                #     > NOTE: the  edit  to  `pga_hba.conf`  needs  to  come
                #     >       **before**  `pg_ctl  start`  (or  the  service
                #     >       needs to be restarted otherwise), because then
                #     >       the changes are not being reloaded.
                #     >
                #     > More info  on setting up and  troubleshooting remote
                #     > PosgreSQL connections (these are  all mirrors of the
                #     > same text; again, paranoia):
                #     >
                #     >   + https://stackoverflow.com/questions/24504680/connect-to-postgres-server-on-google-compute-engine
                #     >   + https://stackoverflow.com/questions/47794979/connecting-to-postgres-server-on-google-compute-engine
                #     >   + https://medium.com/scientific-breakthrough-of-the-afternoon/configure-postgresql-to-allow-remote-connections-af5a1a392a38
                #     >   + https://gist.github.com/toraritte/f8c7fe001365c50294adfe8509080201#file-configure-postgres-to-allow-remote-connection-md
                HOST_COMMON="host\s\+all\s\+all"
                sed -i "s|^$HOST_COMMON.*127.*$|host all all 0.0.0.0/0 trust|" $PGDATA/pg_hba.conf
                sed -i "s|^$HOST_COMMON.*::1.*$|host all all ::/0 trust|"      $PGDATA/pg_hba.conf
                #  + `log*`
                #
                #    > Setting up basic logging,  to see remote connections
                #    > for example.
                #    >
                #    > See the docs for more:
                #    > https://www.postgresql.org/docs/current/runtime-config-logging.html

                pg_ctl                                                  \
                  -D $PGDATA                                            \
                  -l $PGDATA/postgres.log                               \
                  -o "-c unix_socket_directories='$PGDATA'"             \
                  -o "-c listen_addresses='*'"                          \
                  -o "-c log_destination='stderr'"                      \
                  -o "-c logging_collector=on"                          \
                  -o "-c log_directory='log'"                           \
                  -o "-c log_filename='postgresql-%Y-%m-%d_%H%M%S.log'" \
                  -o "-c log_min_messages=info"                         \
                  -o "-c log_min_error_statement=info"                  \
                  -o "-c log_connections=on"                            \
                  start
              '')
              (pkgs.writeShellScriptBin "pg-console" ''
                psql --host $PGDATA -U postgres
              '')

              (pkgs.writeShellScriptBin "pg-mix-setup" ''
                # ####/################################################################
                # # Install Node.js dependencies if not done yet.
                # ####################################################################
                # if test -d "$PWD/assets/" && ! test -d "$PWD/assets/node_modules/"; then
                #   (cd assets && npm install)
                # fi
                ####################################################################
                # If $MIX_HOME doesn't exist, set it up.
                ####################################################################
                if ! test -d $MIX_HOME; then
                  ######################################################
                  # ...  but first,  test whether  there is  a `_backup`
                  # directory. Had issues with  installing Hex on NixOS,
                  # and Hex and  Phoenix can be copied  from there, just
                  # in case.
                  ######################################################
                  if test -d "$PWD/_backup"; then
                    cp -r _backup/.mix .nix-shell/
                  else
                    ######################################################
                    # Install Hex and Phoenix via the network
                    ######################################################
                    yes | ${elixir}/bin/mix local.hex
                    # Install Phoenix
                    # yes | ${elixir}/bin/mix archive.install hex phx_new
                    #TODO:Go to stable whenever it's released
                    yes | ${elixir}/bin/mix archive.install hex phx_new 1.7.0-rc.0
                  fi
                fi
                if test -f "mix.exs"; then
                  # These are not in the  `if` section above, because of
                  # the `hex` install glitch, it  could be that there is
                  # already a `$MIX_HOME` folder. See 2019-08-05_0553
                  ${elixir}/bin/mix deps.get
                  ######################################################
                  # `ecto.setup` is defined in `mix.exs` by default when
                  # Phoenix  project  is  generated via  `mix  phx.new`.
                  # It  does  `ecto.create`,   `ecto.migrate`,  and  run
                  # `priv/seeds`.
                  ######################################################
                  ${elixir}/bin/mix ecto.setup
                fi
              '')

              (pkgs.writeShellScriptBin "update" ''
                nix flake update --commit-lock-file && ${elixir}/bin/mix deps.update --all && ${elixir}/bin/mix deps.get && ${elixir}/bin/mix compile
              '')

              (pkgs.writeShellScriptBin "check-formatted" ''
                cd ${root}

                echo " > CHECKING nix formatting"
                ${pkgs.nixpkgs-fmt}/bin/nixpkgs-fmt *.nix --check
                echo " > CHECKING mix formatting"
                ${elixir}/bin/mix format --check-formatted
              '')
            ]
            ++ pkgs.lib.optional pkgs.stdenv.isLinux pkgs.libnotify # For ExUnit Notifier on Linux.
            ++ pkgs.lib.optional pkgs.stdenv.isLinux pkgs.inotify-tools # For file_system on Linux.
            ++ pkgs.lib.optional pkgs.stdenv.isDarwin pkgs.terminal-notifier # For ExUnit Notifier on macOS.
            ++ pkgs.lib.optionals pkgs.stdenv.isDarwin (
              with pkgs.darwin.apple_sdk.frameworks;
              [
                # For file_system on macOS.
                CoreFoundation
                CoreServices
              ]
            );

          shellHook = ''
            if ! test -d .nix-shell; then
              mkdir .nix-shell
            fi

            export NIX_SHELL_DIR=$PWD/.nix-shell
            # Put the PostgreSQL databases in the project directory.
            export PGDATA=$NIX_SHELL_DIR/db
            # Put any Mix-related data in the project directory.
            export MIX_HOME=$NIX_SHELL_DIR/.mix
            export MIX_ARCHIVES=$MIX_HOME/archives
            export HEX_HOME=$NIX_SHELL_DIR/.hex

            export PATH=$MIX_HOME/bin:$PATH
            export PATH=$HEX_HOME/bin:$PATH
            export PATH=$MIX_HOME/escripts:$PATH
            export LIVEBOOK_HOME=$PWD


            export ERL_LIBS="${erlang}/lib/erlang/lib"

            ${elixir}/bin/mix --version
            ${elixir}/bin/iex --version
          '';
        };
      }
    );
}

mix.exs

defmodule Proj.MixProject do
  use Mix.Project

  def project do
    [
      app: :proj,
      version: "0.1.0",
      elixir: "~> 1.14",
      elixirc_paths: elixirc_paths(Mix.env()),
      start_permanent: Mix.env() == :prod,
      aliases: aliases(),
      deps: deps()
    ]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {Proj.Application, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:phoenix, "~> 1.7.21"},
      {:phoenix_ecto, "~> 4.5"},
      {:ecto_sql, "~> 3.10"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 4.1"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_view, "~> 1.0"},
      {:floki, ">= 0.30.0", only: :test},
      {:phoenix_live_dashboard, "~> 0.8.3"},
      {:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
      {:tailwind, "~> 0.2.0", runtime: Mix.env() == :dev},
      {:heroicons,
       github: "tailwindlabs/heroicons",
       tag: "v2.1.1",
       sparse: "optimized",
       app: false,
       compile: false,
       depth: 1},
      {:swoosh, "~> 1.5"},
      {:finch, "~> 0.13"},
      {:telemetry_metrics, "~> 1.0"},
      {:telemetry_poller, "~> 1.0"},
      {:gettext, "~> 0.26"},
      {:jason, "~> 1.2"},
      {:dns_cluster, "~> 0.1.1"},
      {:bandit, "~> 1.5"}
    ]
  end

  # Aliases are shortcuts or tasks specific to the current project.
  # For example, to install project dependencies and perform other setup tasks, run:
  #
  #     $ mix setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    [
      setup: ["deps.get", "ecto.setup", "assets.setup", "assets.build"],
      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
      "ecto.reset": ["ecto.drop", "ecto.setup"],
      test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
      "assets.setup": ["tailwind.install --if-missing", "esbuild.install --if-missing"],
      "assets.build": ["tailwind proj", "esbuild proj"],
      "assets.deploy": [
        "tailwind proj --minify",
        "esbuild proj --minify",
        "phx.digest"
      ]
    ]
  end
end
water

water OP

found: GitHub - jurraca/elixir-templates: Nix flake templates for Elixir projects · GitHub

and tried to add heroicons using fetchfromgithub.

But I still get the error.

nix build

error: builder for '/nix/store/ak4y1ywr69zn2cd6qhddjdycmfr08hq1-heroicons-2.1.1.drv' failed with exit code 1;
       last 8 log lines:
       > Running phase: unpackPhase
       > unpacking source archive /nix/store/190y6mw4h2bm7bk4knj8aycapyzhdrlx-source
       > source root is source
       > Running phase: patchPhase
       > Running phase: updateAutotoolsGnuConfigScriptsPhase
       > Running phase: configurePhase
       > Running phase: buildPhase
       > ** (Mix) Could not find a Mix.Project, please ensure you are running Mix in a directory with a mix.exs file

flake.nix

{
  description = "A flake template for Phoenix 1.7 projects.";

  inputs = {
    # nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
  };

  outputs =
    {
      self,
      nixpkgs,
    }:
    let
      overlay = prev: final: rec {
        # https://github.com/erlang/otp/security/advisories/GHSA-37cp-fgq5-7wc2
        erlang = prev.beam.interpreters.erlang_27.override {
          version = "27.3.3";
          sha256 = "sha256-OTCCfVeJADxKlmgk8rRE3uzY8Y9qYwY/ubiopWG/0ao=";
          odbcSupport = true; # Define Erlang with ODBC support
        };
        beamPackages = prev.beam.packagesWith erlang;
        elixir = beamPackages.elixir_1_18;
        hex = beamPackages.hex;
        final.mix2nix = prev.mix2nix.overrideAttrs {
          nativeBuildInputs = [ final.elixir ];
          buildInputs = [ final.erlang ];
        };
      };

      forAllSystems = nixpkgs.lib.genAttrs [
        "x86_64-linux"
        "aarch64-linux"
        "x86_64-darwin"
        "aarch64-darwin"
      ];

      nixpkgsFor =
        system:
        import nixpkgs {
          inherit system;
          overlays = [ overlay ];
        };
    in
    {
      packages = forAllSystems (
        system:
        let
          pkgs = nixpkgsFor system;
          # FIXME: import the Mix deps into Nix by running `mix2nix > deps.nix` from a dev shell
          # mixNixDeps = import ./deps.nix {
          #   lib = pkgs.lib;
          #   beamPackages = pkgs.beamPackages;
          # };
          mixNixDeps = import ./deps.nix rec {
            lib = pkgs.lib;
            beamPackages = pkgs.beamPackages;
            overrides = (
              final: prev: {
                # mix2nix does not support git dependencies yet,
                # so we need to add them manually
                heroicons = beamPackages.buildMix {
                  name = "heroicons";
                  version = "2.1.1";

                  src = pkgs.fetchFromGitHub {
                    owner = "tailwindlabs";
                    repo = "heroicons";
                    tag = "v2.1.1";
                    hash = "sha256-4yRqfY8r2Ar9Fr45ikD/8jK+H3g4veEHfXa9BorLxXg=";
                  };
                  beamDeps = [ ];
                };
              }
            );
          };
        in
        {
          default = pkgs.beamPackages.mixRelease {
            pname = "my-phx-app";
            # Elixir app source path
            src = ./.;
            version = "0.1.0";
            # FIXME: mixNixDeps was specified in the FIXME above. Uncomment the next line.
            inherit mixNixDeps;

            # add esbuild and tailwindcss
            buildInputs = [
              pkgs.elixir
              pkgs.esbuild
              pkgs.tailwindcss
            ];

            # Explicitly declare tailwind and esbuild binary paths (don't let Mix fetch them)
            preConfigure = ''
              substituteInPlace config/config.exs \
                --replace "config :tailwind," "config :tailwind, path: \"${pkgs.tailwindcss}/bin/tailwindcss\","\
                --replace "config :esbuild," "config :esbuild, path: \"${pkgs.esbuild}/bin/esbuild\", "
            '';

            # Deploy assets before creating release
            preInstall = ''
              # https://github.com/phoenixframework/phoenix/issues/2690
               mix do deps.loadpaths --no-deps-check, assets.deploy
            '';
          };
        }
      );
      devShells = forAllSystems (
        system:
        let
          pkgs = nixpkgsFor system;
        in
        {
          default = self.devShells.${system}.dev;
          dev = pkgs.callPackage ./shell.nix {
            dbName = "db_dev";
            mixEnv = "dev";
          };
          test = pkgs.callPackage ./shell.nix {
            dbName = "db_test";
            mixEnv = "test";
          };
          prod = pkgs.callPackage ./shell.nix {
            dbName = "db_prod";
            mixEnv = "prod";
          };
        }
      );
    };
}
Zurga

Zurga

The heroicons github repo you are pointing to in your flake.nix is not a mix project.

water

water OP

I got it working to build the project’s flake with nix build , but once I import the flake in my system’s flake it won’t build and shows an error.
Does anyone know what can fix this?

building '/nix/store/qq3lynasxh1v888qddm1bmhvznny3kal-my_new_project-0.1.0.drv'...
error: builder for '/nix/store/qq3lynasxh1v888qddm1bmhvznny3kal-my_new_project-0.1.0.drv' failed with exit code 1;
       last 25 log lines:
       > ==> websock
       > Compiling 1 file (.ex)
       > Generated websock app
       > ==> bandit
       > Compiling 54 files (.ex)
       > Generated bandit app
       > ==> swoosh
       > Compiling 53 files (.ex)
       > Generated swoosh app
       > ==> websock_adapter
       > Compiling 4 files (.ex)
       > Generated websock_adapter app
       > ==> phoenix
       > Compiling 71 files (.ex)
       > Generated phoenix app
       > ==> phoenix_live_view
       > Compiling 39 files (.ex)
       > Generated phoenix_live_view app
       > ==> phoenix_live_dashboard
       > Compiling 36 files (.ex)
       > Generated phoenix_live_dashboard app
       > ==> phoenix_ecto
       > Compiling 7 files (.ex)
       > Generated phoenix_ecto app
       > ln: failed to create symbolic link 'deps/expo/src': File exists
       For full logs, run:
         nix log /nix/store/qq3lynasxh1v888qddm1bmhvznny3kal-my_new_project-0.1.0.drv

commands:

### Mix
mkdir my_new_project
cd my_new_project
yes | mix archive.install hex phx_new
yes | mix phx.new .

# Modify mix.exs by adding the deps_nix to the dependencies:
sed -i '/:phoenix,/a \ \ \ \ \ \ \{:deps_nix, "~> 2.0", only: :dev},' mix.exs
# Modify mix.exs by adding these aliases:
sed -i '/test:\ /a \ \ \ \ \ \ \"deps.update": ["deps.update", "deps.nix"],' mix.exs
sed -i '/test:\ /a \ \ \ \ \ \ \"deps.get": ["deps.get", "deps.nix"],' mix.exs

mix deps.get # Fetch the deps_nix dependency:
mix deps.nix # Generate the initial deps.nix file
git add . # Make files available to the Nix flake
git commit -m "first"

### Build the project
nix build -L

### Create a local database
DATABASE_URL=ecto://postgres:postgres@localhost/my_new_project \
MIX_ENV=prod \
SECRET_KEY_BASE="$(mix phx.gen.secret)" \
mix ecto.create

### Run the built artifact
DATABASE_URL=ecto://postgres:postgres@localhost/my_new_project \
PHX_SERVER=true \
RELEASE_COOKIE=abbccc \
SECRET_KEY_BASE="$(mix phx.gen.secret)" \
./result/bin/my_new_project start

project flake.nix

{
  description = "A flake template for Phoenix 1.7 projects.";

  inputs = {
    nixpkgs.url = "flake:nixpkgs/nixos-unstable";
  };

  outputs =
    { self, nixpkgs }:
    let
      overlay = prev: final: rec {
        erlang = prev.beam.interpreters.erlang_27.override {
          odbcSupport = true;
        };
        beamPackages = prev.beam.packagesWith erlang;
        elixir = beamPackages.elixir_1_18;
        hex = beamPackages.hex;
        final.mix2nix = prev.mix2nix.overrideAttrs {
          nativeBuildInputs = [ final.elixir ];
          buildInputs = [ final.erlang ];
        };
      };

      forAllSystems =
        generate:
        nixpkgs.lib.genAttrs
          [
            "aarch64-darwin"
            "aarch64-linux"
            "x86_64-darwin"
            "x86_64-linux"
          ]
          (
            system:
            generate {
              pkgs = import nixpkgs {
                inherit system;
                overlays = [ overlay ];
              };
            }
          );

      src = ./.;
      mixExs = builtins.readFile "${src}/mix.exs";
      pname = builtins.head (builtins.match ".*app:[[:space:]]*:([a-zA-Z0-9_]+).*" mixExs);
      version = builtins.head (
        builtins.match ".*version:[[:space:]]*\"([0-9]+\\.[0-9]+\\.[0-9]+)\".*" mixExs
      );
    in
    {
      packages = forAllSystems (
        { pkgs, ... }:
        let
          mixNixDeps = pkgs.callPackages ./deps.nix { };
        in
        rec {
          default =
            with pkgs;
            beamPackages.mixRelease {
              inherit pname version src;
              inherit mixNixDeps;

              DATABASE_URL = "";
              SECRET_KEY_BASE = "";

              postBuild = ''
                tailwind_path="$(mix do \
                  app.config --no-deps-check --no-compile, \
                  eval 'Tailwind.bin_path() |> IO.puts()')"
                esbuild_path="$(mix do \
                  app.config --no-deps-check --no-compile, \
                  eval 'Esbuild.bin_path() |> IO.puts()')"

                ln -sfv ${tailwindcss}/bin/tailwindcss "$tailwind_path"
                # ln -sfv ${tailwindcss_4}/bin/tailwindcss "$tailwind_path"
                ln -sfv ${esbuild}/bin/esbuild "$esbuild_path"
                ln -sfv ${mixNixDeps.heroicons} deps/heroicons

                mix do \
                  app.config --no-deps-check --no-compile, \
                  assets.deploy --no-deps-check
              '';
            };

          nixosModule = { config, lib, pkgs, ... }: {
              config = {
                environment.systemPackages = with pkgs; [
                  default
                ];
              };
            };
        }
      );

      devShells = forAllSystems (
        { pkgs, ... }:
        {
          default = self.devShells.${pkgs.system}.dev;
          dev = pkgs.callPackage ./shell.nix {
            dbName = "db_dev";
            mixEnv = "dev";
          };
          test = pkgs.callPackage ./shell.nix {
            dbName = "db_test";
            mixEnv = "test";
          };
          prod = pkgs.callPackage ./shell.nix {
            dbName = "db_prod";
            mixEnv = "prod";
          };
        }
      );
    };
}

project shell.nix

{
  pkgs,
  dbName,
  mixEnv,
  beamPackages,
}:
let
  # define packages to install
  basePackages = with pkgs; [
    git
    elixir
    elixir-ls
    hex
    mix2nix
    postgresql
    # pgcli
    esbuild
    nodePackages.prettier # Formatting .js file
    tailwindcss
    # tailwindcss_4
    # codespell --skip="./deps/*,./.git/*,./assets/*,./erl_crash.dump" -w
    codespell
    # dot -Tpng ecto_erd.dot -o erd.png
    pkgs.graphviz

    unixODBC
  ];

  # Add basePackages + optional system packages per system
  inputs =
    with pkgs;
    basePackages
    ++ lib.optionals stdenv.isLinux [ libnotify ] # For ExUnit Notifier on Linux.
    ++ lib.optionals stdenv.isLinux [ inotify-tools ] # For file_system on Linux.
    ++ lib.optionals stdenv.isDarwin [ terminal-notifier ] # For ExUnit Notifier on macOS.
    ++ lib.optionals stdenv.isDarwin (
      with darwin.apple_sdk.frameworks;
      [
        # For file_system on macOS.
        CoreFoundation
        CoreServices
      ]
    );

  # define shell startup command
  hooks = ''
    # this allows mix to work on the local directory
    mkdir -p .nix-mix .nix-hex
    export MIX_HOME=$PWD/.nix-mix
    export HEX_HOME=$PWD/.nix-mix
    export PATH=$MIX_HOME/bin:$HEX_HOME/bin:$PATH

    export MIX_ENV=${mixEnv}

    export LANG=en_US.UTF-8
    # keep your shell history in iex
    export ERL_AFLAGS="-kernel shell_history enabled"

    # postgres related
    # keep all your db data in a folder inside the project
    export PGDATA="$PWD/db"

    # phoenix related env vars
    export POOL_SIZE=15
    export DB_URL="postgresql://postgres:postgres@localhost:5432/${dbName}"
    export PORT=4000
  '';
in
pkgs.mkShell {
  buildInputs = inputs;
  shellHook = hooks;
}

system flake.nix

{
  inputs = {
    nixpkgs.url = "flake:nixpkgs/nixos-unstable";
    my_new_project.url = "path:/mypath/my_new_project";
    # ...
  };

  # ...
}

a system module

{
  config,
  lib,
  inputs,
  ...
}:
with lib;
{
  imports = [
    inputs.my_new_project.outputs.packages.x86_64-linux.nixosModule
  ];

  config = {
  }
}

inspiration: GitHub - code-supply/nix-phoenix: Tools and instructions for starting Phoenix projects with Nix deployment · GitHub

Zurga

Zurga

I think the problem is that the deps folder already exists in the build environment. This could be because the flake.nix has src = ./.;. This is a great resource for learning how to include only the files you need in the build environment: Working with local files — nix.dev documentation

To clarify a bit more, buildMix is going to get all the deps and compile them again based on the deps.nix contents.

water

water OP

      src = pkgs.nix-gitignore.gitignoreSource [
        "/flake.nix"
        "/flake.lock"
        "/shell.nix"
        "/README.md"
        "/.git/"
        # TODO: add extra patterns besides ones specified by .gitignore, such as /fly.toml
      ] ./.;

Now it nixos-rebuild without errors.

I just have to check the systemd service.

Thank you.

Zurga

Zurga

Good to hear you got it working! I have gone through a similar process myself :wink:.

I implemented the SystemD services in three different parts for deployment on my server :

  • seed service (runs only once),
  • migration service (runs after the seed service and is restarted whenever the web service is restarted),
  • web service (depends on the migration service to exits successfully).

If you want, you can structure the NixOS module in a way that you setup the database, nginx proxy, ssl from one configuration.

In my NixOS config I can do the following:

services.my-project = {
  prod = {
    branch = "master";
    commit = "commit ref";
    port = "4000";
    host = "example.com";
  };
  staging = {
    branch = "dev";
    commit = "commit ref on dev branch";
    port = "4001";
    host = "staging.example.com";
  };
};

Any branch can be used to create a domain name with minimal configuration effort. Database names are just the name of the app with the branch as a suffix (maybe I will allow a explicit suffix in the config in the future). Having to specify the port is something I want to get rid of and just use Unix sockets instead. Using Systemd socket should allow for zero-downtime deployments, but there is still work to be done with regard to gen_tcp.

If you want to see how it works, I can post the aforementioned module in this thread.

water

water OP

Please do share.

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apz
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews