NixOS
Sandhole is available as a flake, containing an overlay and a NixOS service.
Setup
If you’re using Nix Flakes for your system, you can install the NixOS service like so:
{
description = "My NixOS config";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
# ...
sandhole.url = "github:EpicEric/sandhole";
};
outputs =
{
nixpkgs,
sandhole,
...
}@inputs:
{
nixosConfigurations."your-hostname" = nixpkgs.lib.nixosSystem {
specialArgs = { inherit inputs; };
modules = [
./configuration.nix
# ...
sandhole.nixosModules.sandhole
];
};
};
}
Here’s an example configuration.nix with Sandhole and Agnos. You can find full options in the NixOS module options page:
{
pkgs,
...
}:
let
# ...
# Add admin keys to this directory
adminKeysDirectory = pkgs.linkFarm "sandhole-admin-keys" [
{
name = "example-admin.pub";
path = pkgs.writeText "example-admin.pub" ''
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPH3e5SFdwLOuleypjfgauqEUAmgpm9r8lqfvc6G1o1D example-admin
'';
}
];
# Add user keys to this directory
userKeysDirectory = pkgs.linkFarm "sandhole-user-keys" [
{
name = "example-user.pub";
path = pkgs.writeText "example-user.pub" ''
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtH7kS+q8/8TXWAp4OJvRh/7GNkQ6FR/QBOhGJuEwEC example-user
'';
}
];
certificatesDirectory = "/var/lib/sandhole/certificates";
in
{
# ...
services.sandhole = {
# Install the Sandhole package and enable the systemd service
enable = true;
# Let Sandhole manage the firewall and open ports from its configuration.
# Note: If `disableTcp` is `false` (default), it will open all ports >= 1024
openFirewall = true;
# These are the same CLI options from Sandhole, except in camelCase.
# See: http://sandhole.com.br/nixos_options.html
# Make sure to change at least `domain` and `acmeContactEmail` below
settings = {
domain = "sandhole.com.br";
acmeContactEmail = "admin@sandhole.com.br";
disableTcp = true;
forceHttps = true;
inherit
adminKeysDirectory
userKeysDirectory
certificatesDirectory
;
};
};
security.agnos = {
enable = true;
temporarilyOpenFirewall = true;
user = "sandhole";
generateKeys.enable = true;
settings =
{
dns_listen_addr = "[::]:53";
accounts =
[
{
# Change this to your e-mail address
email = "admin@sandhole.com.br";
private_key_path = "./letsencrypt_key.pem";
certificates =
[
{
# Change these from `sandhole.com.br` to your domain
domains = [ "sandhole.com.br" "*.sandhole.com.br" ];
fullchain_output_file = "${certificatesDirectory}/sandhole.com.br/fullchain.pem";
key_output_file = "${certificatesDirectory}/sandhole.com.br/privkey.pem";
}
];
}
];
};
};
}
You can then connect services with the provided keys. For example, to use a Vaultwarden NixOS container in the same machine:
{
lib,
...
}:
{
# ...
networking.nat = {
enable = true;
internalInterfaces = ["ve-+"];
externalInterface = "eno0"; # Change to the appropriate interface
enableIPv6 = true;
};
# Example: Setting up Vaultwarden
containers.vaultwarden = {
autoStart = true;
privateNetwork = true;
hostAddress = "192.168.102.1";
localAddress = "192.168.102.2";
hostAddress6 = "fc00::2:1";
localAddress6 = "fc00::2:2";
extraFlags = [ "-U" ];
config =
{ lib, ... }:
{
services.vaultwarden = {
enable = true;
config = {
DOMAIN = "https://vaultwarden.sandhole.com.br";
SIGNUPS_ALLOWED = false;
ROCKET_ADDRESS = "::";
ROCKET_PORT = 8222;
ROCKET_LOG = "warning";
};
};
networking = {
firewall.allowedTCPPorts = [ 8222 ];
useHostResolvConf = lib.mkForce false;
};
services.resolved.enable = true;
system.stateVersion = "25.11";
};
};
# Proxy Vaultwarden to the local Sandhole instance
services.autossh.sessions = [
{
name = "vaultwarden";
user = "root";
# Change the arguments as necessary
extraArguments = ''
-i /path/to/ssh/key/example-user \
-o StrictHostKeyChecking=accept-new \
-o ServerAliveInterval=30 \
-R vaultwarden.sandhole.com.br:80:192.168.102.2:8222 \
-p 2222 \
127.0.0.1
'';
}
];
}
Binary caching
In order to avoid re-building Sandhole for each update, you can use the Sandhole binary cache. In configuration.nix:
nix.settings = {
substituters = [
"https://nix-community.cachix.org"
"https://sandhole.cachix.org"
];
trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
"sandhole.cachix.org-1:cZadr6kgjQcRvsr++Nv9kgtMOrbLahiZBpuI9WpIXvA="
];
};