Vps Ultraman

vps-ultraman Host Configuration

Role: Dedicated Sing-box Proxy Node
Platform: x86_64-linux | RAM: 962MB | Disk: 20GB

Bootstrap History

This host required multiple bootstrap attempts to solve cascading issues from running NixOS on a 1GB VPS. The final successful run used nixos-anywhere --no-disko-deps with a pre-bootstrap ZRAM setup.

Issues Encountered & Solutions

1. OOM during nix copy (disko deps)

Symptom: SSH to installer times out during nix copy --to. Cause: NixOS installer’s /nix/store is a tmpfs overlay capped at 482MB (half of 962MB RAM). Copying disko dependencies (~400MB) exhausts it. Fix: Use --no-disko-deps flag — disko partitioning tools come from the kexec installer image instead of being copied to tmpfs.

2. udevadm SIGBUS crash during disko

Symptom: udevadm trigger --subsystem-match=block crashes with bus error during disko execution. Cause: Kernel partition table inconsistency after sgdisk changes. udevadm accesses sectors beyond the new partition boundary. Fix: Background helper patches the disko script to skip udevadm trigger/settle calls (replace with sleep 2).

3. System closure too large for tmpfs

Symptom: OOM during nix copy of system closure (originally 7.6GB). Fix (multi-step):

  • Removed desktop tools from hey.nix unconditional systemPackages (hyprland, grimblast, zbar, etc.) → saved ~4.5GB
  • Disabled hardware.enableRedistributableFirmware on cpu/qemu profile → saved ~787MB
  • Disabled AppArmor on this host (security.hardening = false)
  • Final closure: 2.6GB (down from 7.6GB)
  • Background helper switches nix store to persistent btrfs overlay after disko creates /mnt/nix

4. SSH not available after boot

Symptom: sshd.service: start request repeated too quickly. Failed to start SSH Daemon. /etc/ssh/sshd_config: No such file or directory Cause: environment.persistence."/persist" bind-mounted an empty /persist/etc/ssh over /etc/ssh on first boot, hiding the sshd_config symlink created by services.openssh via environment.etc. The impermanence mount runs at local-fs.target, before NixOS activation. Fix: Changed from directory-level to file-level persistence:

environment.persistence."/persist".files = [
  "/etc/ssh/ssh_host_ed25519_key"
  "/etc/ssh/ssh_host_ed25519_key.pub"
];

Uses startWhenNeeded = true (socket activation) — sshd starts on-demand via sshd.socket, saving resources on a 1GB VPS.

5. No TTY login after bootstrap

Symptom: Booted to TTY login prompt but no password set. Cause: Root password only available via agenix, which requires pre-existing host keys. Fix: Added password.mode = "bootstrap" option — sets users.users.root.initialPassword = "nixos" for first-boot access.

Working Bootstrap Command

# One-shot script (handles pre/post phases):
./scripts/bootstrap_vps-ultraman.sh

# Equivalent hey CLI (the script runs this internally):
# hey ops bootstrap --no-disko-deps vps-ultraman vps_ultraman_root

# Equivalent raw CLI:
# nix run github:nix-community/nixos-anywhere -- \
#     --no-disko-deps \
#     --flake .#vps-ultraman \
#     vps_ultraman_root

The script phases:

  1. Preps the VPS (cpio, 4GB ZRAM + 4GB disk swap)
  2. Launches a background helper that patches disko and switches to persistent overlay
  3. Runs nixos-anywhere in the foreground
  4. Cleans up on exit

Post-Bootstrap

  1. SSH in with your key (configured in alienzj.nix authorized_keys)
  2. If SSH fails, login at TTY with root / nixos
  3. Change security.password.mode from "bootstrap" to "deploy"
  4. Deploy updates:
# Activate immediately:
hey ops deploy vps-ultraman vps_ultraman_root

# Safer: activate on next reboot:
hey ops deploy vps-ultraman vps_ultraman_root --boot

# Equivalent raw CLI (boot):
# nixos-rebuild boot --show-trace \
#     --flake .#vps-ultraman \
#     --target-host root@vps_ultraman_root \
#     --build-host localhost

Configuration Summary

modules = {
  security = {
    hardening = false;           # No AppArmor on VPS
    nameservers = ["1.1.1.1" "8.8.8.8"];
    password.mode = "bootstrap"; # First-boot TTY access
  };
  services.net = {
    ssh.enable = true;           # Socket-activated sshd (startWhenNeeded=true)
    sing-box = {
      enable = true;
      tun.enable = false;
      webPanel.enable = false;
      ports = [40443 50443 60443];
    };
  };
};