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

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
Onor.io
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
Trolleger
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
matt-savvy
Anyone here using Honeybadger? My Honeybadger account is being overwhelmed with noise from some bots. Seeing a lot of Bandit.HTTPError...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New

Other Trending Topics Top

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
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews