For NixOS on AWS EC2, how to get IP address

When running NixOS on AWS EC2, and populating /etc/nixos/configuration.nix via user data, there are times when you need to refer to the instance’s IP address (private, not EIP or something complicated).

How does one lookup and provide the IP as a value, to be used for parameters defined in configuration.nix?

ip a | grep inet. You should be able to find your address i guess.

I guess what I’m not understanding is how you would integrate that into configuration.nix such that you could use that value, pass it as a variable to a module, etc. Are you suggesting we can embed shell straight into configuration.nix?

You can use pkgs.runCommand to write the ip out, and use it in the configuration if you want:

  pkgs.runCommand "ip" { } ''
   # Do your filtering here
   ip a > "$out";
  '')

I will give that a try! Thanks for the tip :slight_smile:

When I try to fit that into my configuration.nix, I get:

/build/.attr-0: line 1: curl: command not found

I’m not seeing any shell command work, so same with ip, for example:

/build/.attr-0: line 1: ip: command not found

Am I doing it wrong? Do I need to provide a full path to curl/etc?

Trying to get my nixOS local IP in my configuration too.

With this I could get it to run a script that should get the IP address, but it would just give the localhost address

let
        ipDerivation = pkgs.runCommand "${pkgs.busybox}/bin/ip" {} ''
          ${pkgs.busybox}/bin/ip a | grep "scope" | grep -Po '(?<=inet )[\d.]+' | head -n 2 | tail -n 1 > "$out";
          echo "$out";
        '';
        ipAddress = builtins.readFile ipDerivation;
      in

it seems like the environment it runs that script doesn’t have access to the internet, so it can’t get the local IP. This code just gives NETWOR UNREACHABLE:

let
        ipDerivation = pkgs.runCommand "${pkgs.busybox}/bin/ip" {} ''
          ${pkgs.busybox}/bin/ip -4 route get 8.8.8.8 | awk '{print $7}' > "$out";
          echo "$out";
        '';
        ipAddress = builtins.readFile ipDerivation;
      in

You can’t get your machines DHCP assigned IP address within the configuration. That is considered impure.

You can either hardcode your network configuration and re use the configuration values where ever else you need them, or you need to find ways to cope with not knowing the address.

Perhaps you can describe the actual problem you want to solve?

2 Likes

In my case my configuration already needs --impure because of this line in a module I made and am using: frida = (builtins.getFlake "github:itstarsun/frida-nix").packages.x86_64-linux.frida-tools;.
But still, I’d prefer to do it the right way.
The problem is also in that module, I’m running this docker program that communicates with a VM running on my PC, and it needs my IP in the environoment:

      virtualisation.oci-containers.containers = {
        fastapi-dls = {
          image = "collinwebdesigns/fastapi-dls:latest";
          volumes = [
            "${cfg.fastapi-dls.docker-directory}/fastapi-dls/cert:/app/cert:rw"
            "dls-db:/app/database"
          ];
          # Set environment variables
          environment = {
            TZ = "${cfg.fastapi-dls.timezone}";
            DLS_URL = "${cfg.fastapi-dls.local_ipv4}"; # this should grab your hostname or your IP!
            DLS_PORT = "443";
            LEASE_EXPIRE_DAYS="90";
            DATABASE = "sqlite:////app/database/db.sqlite";
            DEBUG = "true";
          };
          extraOptions = [
          ];
          # Publish the container's port to the host
          ports = [ "443:443" ];
          # Automatically start the container
          autoStart = true;
        };
      };

My ideas are, making my configuration have a static IP and hardcoding it as suggested (tho it’s a laptop, and I’m not sure if I change networks or change to a wired connection if the IP stays the same), Not use docker and compile the program, or finding a way to do it with the --impure argument

Either way, you’ve answered the question in the post, so I guess the only thing left to answer here would be maybe some way to do this with the --impure argument

Your best bet, when using --impure anyway, is to use an environment variable that you set when you rebuild.

And I really consider it weird that you need some local IP of your host to connect to a VM… Proper way would be to use DNS here.

Anyway, this derails this thread.

You have 2 problems which you should solve both. --impure is always a red flag.

1 Like

Yeah, it’s not to connect, it’s abot sharing the GPU with the VM.

But we’ll keep it here for this thread.

Thanks a lot :blush:

Maybe you can use the virtualisation.oci-containers.containers.<name>.environmentFiles option for that. At boot (or in a systemd timer if your ip address changes a lot, or even better, whenever you detect that it changes) run a script that writes your ip address to some file, e.g. /my-ip, and then set environmentFiles = ["/my-ip"]; in your container configuration.

You should instead define the itstarsun/frida-nix input in your flake.nix and then pass it into default.nix to make this pure, for example:

# flake.nix
{
  description = "NixOS module which provides NVIDIA vGPU functionality";

  inputs.frida.url = "github:itstarsun/frida-nix";

  outputs = { self, frida }: {
    nixosModules.nvidia-vgpu = import ./default.nix frida;
  };
}
# default.nix
fridaFlake: { pkgs, lib, config, buildPythonPackage, ... }:

let
  frida = fridaFlake.packages.${pkgs.system}.frida-tools;
# ...rest of your `default.nix`
1 Like

Wow, thank you so much for that flake input solution! I had tried it before but didn’t know how to pass it to the default.nix file! :heart:
(unfortunately, it looks like it still needs impure because of docker :person_facepalming: error: access to absolute path '/opt/docker' is forbidden in pure eval mode (use '--impure' to override))

As for the IP, I’m content with hardcoding the IP and setting a static IP for now:

  networking.interfaces.eth0.ipv4.addresses = [ {
    address = "192.168.1.109";
    prefixLength = 24;
  } ];

But if applying your suggestion, I had trouble making systemd services interact with files on my system before, because they also seemed to be restricted in a “sandboxed” environment, much like pkgs.runCommand seemed to be. But I just tried it again with this code and managed to create a /my-ip file successfully so all’s good:

  systemd.services.my-awesome-service = {
    description = "writes a file to /my-ip";
    serviceConfig.PassEnvironment = "DISPLAY";
    script = ''
      ${pkgs.busybox}/bin/ip a | grep "scope" | grep -Po '(?<=inet )[\d.]+' | head -n 2 | tail -n 1 > /my-ip;
      echo "success!"
    '';
    wantedBy = [ "multi-user.target" ]; # starts after login
  };

Thanks :heart:

You’re welcome :slight_smile: So how import works is that you can basically replace import <path> with (<content of path>) where path can be a .nix file or a directory containing a default.nix. So in this case, the

import ./default.nix frida

would turn into

(fridaFlake: { pkgs, lib, ... }: { ... }) frida

so an anonymous function that gets called with the frida argument. Hope this helps :slight_smile:

Maybe ProtectSystem was enabled on those services? Could of course be a lot of things…

1 Like