NixOS Containers: Difference between revisions

Consistent formatting
Line 4: Line 4:


=== Host Configuration ===
=== Host Configuration ===
For all of the examples below to work, you'll have to enable virtualization and the use of containers in your host systems nix configuration.{{file|||<nowiki>
For all of the examples below to work, you'll have to enable virtualization and the use of containers in your host systems nix configuration.
boot.enableContainers = true;
{{file|3=boot.enableContainers = true;
virtualisation.containers.enable = true;
virtualisation.containers.enable = true;|name=/etc/nixos/configuration.nix|lang=nix}}
</nowiki>|name=/etc/nixos/configuration.nix|lang=nix}}


=== Configuration ===
=== Configuration ===


The following example creates a container called webserver running a httpd web server. It will start automatically at boot and has its private network subnet.
The following example creates a container called webserver running a httpd web server. It will start automatically at boot and has its private network subnet.
{{file|3=networking.nat = {
{{file|3=networking.nat = {
   enable = true;
   enable = true;
Line 47: Line 45:
     system.stateVersion = "24.11";
     system.stateVersion = "24.11";
   };
   };
};|name=configuration.nix|lang=nix}}
};|name=/etc/nixos/configuration.nix|lang=nix}}


In order to reach the web application on the host system, we have to open [[Firewall]] port 80 and also configure NAT through <code>networking.nat</code>. The web service of the container will be available at http://192.168.100.11
In order to reach the web application on the host system, we have to open [[Firewall]] port 80 and also configure NAT through <code>networking.nat</code>. The web service of the container will be available at http://192.168.100.11
Line 58: Line 56:


===== NAT (Network Address Translation) =====
===== NAT (Network Address Translation) =====
In order to allow the container to connect to the internet, you have to configure NAT through <code>networking.nat</code>.
{{File|3=networking.nat = {
{{File|3=networking.nat = {
   enable = true;
   enable = true;
Line 64: Line 63:
   # Lazy IPv6 connectivity for the container
   # Lazy IPv6 connectivity for the container
   enableIPv6 = true;
   enableIPv6 = true;
};|name=configuration.nix|lang=nix}}
};|name=/etc/nixos/configuration.nix|lang=nix}}


===== Bridge =====
===== Bridge =====
<syntaxhighlight lang="nix">
Connect a container to a bridge using Network Manager interfaces:
networking = {
{{File|3=networking = {
   bridges.br0.interfaces = [ "eth0s31f6" ]; # Adjust interface accordingly
   bridges.br0.interfaces = [ "eth0s31f6" ]; # Adjust interface accordingly
    
    
Line 89: Line 88:
   localAddress = "192.168.100.5/24";
   localAddress = "192.168.100.5/24";
   config = { };
   config = { };
};
};|name=/etc/nixos/configuration.nix|lang=nix}}
</syntaxhighlight>


===== Without privateNetwork (simpler) =====
===== Without privateNetwork (simpler) =====
If the service can be accessed by changing its port, the private network is not needed necessarily. Be careful to not use occupied ports. This example runs an [[Actual]] server on port 3003. It can be accessed through the host at <code>http://localhost:3003</code>. Since <code>privateNetwork</code> is not defined, it defaults to <code>false</code>.
If the service can be accessed by changing its port, the private network is not needed necessarily. Be careful to not use occupied ports. This example runs an [[Actual]] server on port 3003. It can be accessed through the host at <code>http://localhost:3003</code>. Since <code>privateNetwork</code> is not defined, it defaults to <code>false</code>.


<syntaxhighlight lang="nix">
{{File|3=containers.actualContainer = {
containers.actualContainer = {
   autoStart = true;
   autoStart = true;
   config = {...}: {
   config = {...}: {
Line 104: Line 101:
     };
     };
   };
   };
};
};|name=/etc/nixos/configuration.nix|lang=nix}}
</syntaxhighlight>


=== Usage ===
=== Usage ===
Line 135: Line 131:
</syntaxhighlight>
</syntaxhighlight>


View log for container<syntaxhighlight lang="console">
View log for container
<syntaxhighlight lang="console">
# journalctl -M webserver
# journalctl -M webserver
</syntaxhighlight>Further informations are available in the {{manual:nixos|sec=#ch-containers|chapter=NixOS manual}}.
</syntaxhighlight>
Further informations are available in the {{manual:nixos|sec=#ch-containers|chapter=NixOS manual}}.


== Tips and tricks ==
== Tips and tricks ==
Line 143: Line 141:
=== Define and create nixos-container from a Flake file ===
=== Define and create nixos-container from a Flake file ===


We can define and create a custom container called <code>container</code> from a file stored as <code>flake.nix</code>. In this case we use the unstable branch of the nixpkgs repository as a source.<syntaxhighlight lang="nix">
 
{
We can define and create a custom container called <code>container</code> from a file stored as <code>flake.nix</code>. In this case we use the unstable branch of the nixpkgs repository as a source.
{{File|3={
   inputs.nixpkgs.url = "nixpkgs/nixos-unstable";
   inputs.nixpkgs.url = "nixpkgs/nixos-unstable";


Line 166: Line 165:


   };
   };
}
}|name=/etc/nixos/configuration.nix|lang=nix}}
</syntaxhighlight>To create and run that container, enter following commands. In this example the <code>flake.nix</code> file is in the same directory.<syntaxhighlight lang="console">
 
To create and run that container, enter following commands. In this example the <code>flake.nix</code> file is in the same directory.
<syntaxhighlight lang="console">
# nixos-container create flake-test --flake .
# nixos-container create flake-test --flake .
host IP is 10.233.4.1, container IP is 10.233.4.2
host IP is 10.233.4.1, container IP is 10.233.4.2
Line 176: Line 177:
=== Use agenix secrets in container ===
=== Use agenix secrets in container ===


To add <code>agenix</code> secrets to a container bind mount the <code>ssh-host.key</code> and import the <code>agenix.nixosModule</code> and set <code>age.identityPaths</code> [https://discourse.nixos.org/t/secrets-inside-nixos-containers/34403/6 Source]<syntaxhighlight lang="nix">
 
{ agenix, ... }:
To add <code>agenix</code> secrets to a container bind mount the <code>ssh-host.key</code> and import the <code>agenix.nixosModule</code> and set <code>age.identityPaths</code> [https://discourse.nixos.org/t/secrets-inside-nixos-containers/34403/6 Source]
{{File|3={ agenix, ... }:
{
{


Line 202: Line 204:
       };
       };
   };
   };
}
}|name=/etc/nixos/configuration.nix|lang=nix}}
</syntaxhighlight>


=== Bridge together two nixos-containers with privateNetwork enabled ===
=== Bridge together two nixos-containers with privateNetwork enabled ===
'''Target:'''
'''Target:'''


Create two containers, both with <code>privateNetwork = true;</code>
Create two containers, both with <code>privateNetwork = true;</code>:


* <code>containerA</code> at 192.168.100.2
* <code>containerA</code> at 192.168.100.2
Line 215: Line 216:
** which runs an httpd server at http://localhost:80
** which runs an httpd server at http://localhost:80


They should be connected with a bridge <code>br0</code> and both should have internet address
They should be connected with a bridge <code>br0</code> and both should have internet address.
 
Assuming Network Manager is used, so the introduction of systemd.network should not interfere with the rest of the setup.


'''Configuration:'''
'''Configuration:'''


Create and configure the bridge and the internet connection
Create and configure the internet connection and the bridge:
{{File|3=# Give containers access to the internet
{{File|3=# Give containers access to the internet
networking.nat = {
networking.nat = {
   enable = true;
   enable = true;
  # Use "ve-*" when using nftables instead of iptables
   internalInterfaces = [ "br0" ]; # Connect the bridge to the internet
   internalInterfaces = [ "br0" ]; # Connect the bridge to the internet
   externalInterface = "wlp5s0";  # Adjust according to your internet interface
   externalInterface = "wlp5s0";  # Adjust according to your internet interface
Line 235: Line 237:
# set systemd.network.wait-online.enable to false so that boot isn't blocked on connectivity that networkd will never provide.
# set systemd.network.wait-online.enable to false so that boot isn't blocked on connectivity that networkd will never provide.
# https://wiki.nixos.org/wiki/Systemd/networkd#When_to_use
# https://wiki.nixos.org/wiki/Systemd/networkd#When_to_use
boot.initrd.systemd.network.wait-online.enable = false;
systemd.network = {
systemd.network = {
   enable = true;
   enable = true;
Line 280: Line 281:
     };
     };
   };
   };
};|name=configuration.nix|lang=nix}}
};|name=/etc/nixos/configuration.nix|lang=nix}}


Create and configure <code>containerA</code>
Create and configure <code>containerA</code>:
{{File|3=containers.containerA = {
{{File|3=containers.containerA = {
   autoStart = true;
   autoStart = true;
Line 295: Line 296:
        
        
       networking = {
       networking = {
         # changes the gateway to the Network Address of the bridge, so that it has access to the internet
         # Changes the gateway to the Network Address of the bridge, so that it has access to the internet
         # the bridge has access to the internet
         # The bridge has access to the internet
         defaultGateway = {
         defaultGateway = {
           address = "192.168.100.1";
           address = "192.168.100.1";
Line 310: Line 311:
       services.resolved.enable = true;
       services.resolved.enable = true;
     };
     };
};|name=configuration.nix|lang=nix}}
};|name=/etc/nixos/configuration.nix|lang=nix}}


Create and configure <code>containerB</code>
Create and configure <code>containerB</code>:
{{File|3=containers.containerB = {
{{File|3=containers.containerB = {
   autoStart = true;
   autoStart = true;
Line 330: Line 331:
        
        
       networking = {
       networking = {
         # changes the gateway to the Network Address of the bridge, so that it has access to the internet
         # Changes the gateway to the Network Address of the bridge, so that it has access to the internet
         # the bridge has access to the internet
         # The bridge has access to the internet
         defaultGateway = {
         defaultGateway = {
           address = "192.168.100.1";
           address = "192.168.100.1";
Line 346: Line 347:
       services.resolved.enable = true;
       services.resolved.enable = true;
     };
     };
};|name=configuration.nix|lang=nix}}
};|name=/etc/nixos/configuration.nix|lang=nix}}


You can test the connection by loggining into containerA and pinging containerB, curling to its httpd server or pinging an internet website:<syntaxhighlight lang="console"># nixos-container root-login containerA
You can test the connection by loggining into containerA and pinging containerB, curling to its httpd server or pinging an internet website:
<syntaxhighlight lang="console">
# nixos-container root-login containerA
[root@containerA:~]# ping 192.168.100.3 -c3 # ping containerB
[root@containerA:~]# ping 192.168.100.3 -c3 # ping containerB
[root@containerA:~]# curl http://192.168.100.3:80 # curl to containerB's httpd server
[root@containerA:~]# curl http://192.168.100.3:80 # curl to containerB's httpd server
[root@containerA:~]# ping nixos.org -c3 # ping an internet website</syntaxhighlight>
[root@containerA:~]# ping nixos.org -c3 # ping an internet website
</syntaxhighlight>


== Troubleshooting ==
== Troubleshooting ==