├── .gitignore ├── generate-openvz-tarball.nix ├── flake.lock ├── flake.nix ├── LICENSE ├── README.md ├── installer.nix └── nixos.nix /.gitignore: -------------------------------------------------------------------------------- 1 | .direnv 2 | result 3 | -------------------------------------------------------------------------------- /generate-openvz-tarball.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? import { system = "x86_64-linux"; overlays = []; }, 3 | configuration ? {}, 4 | }: 5 | let 6 | nixos = pkgs.nixos { 7 | imports = [ 8 | configuration 9 | ./nixos.nix 10 | ./installer.nix 11 | ]; 12 | }; 13 | in nixos.config.system.build.tarball 14 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1657533762, 6 | "narHash": "sha256-/cxTFSMmpAb8tBp1yVga1fj+i8LB9aAxnMjYFpRMuVs=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "38860c9e91cb00f4d8cd19c7b4e36c45680c89b5", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "nixos-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = ""; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 | }; 7 | 8 | outputs = { self, nixpkgs }: let 9 | pkgs = nixpkgs.legacyPackages.x86_64-linux; 10 | inherit (pkgs) lib; 11 | in { 12 | nixosModule = self.nixosModules.ovz-container; 13 | nixosModules = rec { 14 | default = ovz-container; 15 | ovz-container = import ./nixos.nix; 16 | ovz-installer = import ./installer.nix; 17 | }; 18 | 19 | lib.generateOpenVzTarball = let 20 | f = lib.makeOverridable (import ./generate-openvz-tarball.nix) { 21 | inherit pkgs; 22 | }; 23 | in f.override; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Zhaofeng Li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NixOS on OpenVZ 7 2 | 3 | Configurations needed to run NixOS on OpenVZ 7 VPSes created with the Debian/Ubuntu template. 4 | 5 | This repo is only useful if you are an end-user without access to the host and it's unreasonable to request configuration changes on the host. 6 | If you are the provider, please [create a new distribution config](https://discourse.nixos.org/t/nixos-as-openvz-7-guest/10683/2) that does not attempt to manipulate the guest configurations. 7 | 8 | ## Usage 9 | 10 | Create a `configuration.nix`: 11 | 12 | ```nix 13 | { 14 | networking.useNetworkd = true; 15 | 16 | systemd.network.networks.venet0 = { 17 | name = "venet0"; 18 | # Change to your assigned IP 19 | address = [ "10.10.10.123/32" ]; 20 | networkConfig = { 21 | DHCP = "no"; 22 | DefaultRouteOnDevice = "yes"; 23 | ConfigureWithoutCarrier = "yes"; 24 | }; 25 | }; 26 | 27 | services.openssh.enable = true; 28 | 29 | users.users.root.openssh.authorizedKeys.keyFiles = [ 30 | # ... 31 | ]; 32 | } 33 | ``` 34 | 35 | Next, build the tarball that contains the bootstrap configuration: 36 | 37 | 38 | ``` 39 | nix-build generate-openvz-tarball.nix --arg configuration ./configuration.nix 40 | ``` 41 | 42 | Upload the tarball to the VPS, then extract it onto the root filesystem: 43 | 44 | ``` 45 | tar xpf nixos-system-x86_64-linux.tar.xz -C / 46 | reboot -f 47 | ``` 48 | 49 | The VPS will reboot into NixOS, with existing files in the root filesystem moved into `/old-root`. 50 | You can delete the directory to save space. 51 | When rebuilding, include `./nixos.nix` in your NixOS configuration. 52 | 53 | ## Tested Providers 54 | 55 | - [Gullo's Hosting](https://hosting.gullo.me) 56 | - [Inception Hosting](https://inceptionhosting.com) 57 | - [EthernetServers](https://www.ethernetservers.com) 58 | - [WebHorizon](https://webhorizon.in) 59 | - [STRATO](https://www.strato.de/) 60 | 61 | ## FAQ 62 | 63 | ### Why is this needed? 64 | 65 | With most out-of-box templates, OpenVZ automatically runs a set of bash scripts in the guest container prior to every boot to customize the system (setting hostname, IP addresses, etc.). 66 | We can't run them in NixOS, but the scripts have to be successfully executed for the container to boot :( 67 | 68 | Here we silently ignore the scripts with an ugly hack, which is a `/bin/bash` wrapper that refuses to do anything if PID 1 is `vzctl`. 69 | Note that `vzctl enter` as well as the "Serial Console" feature in SolusVM also hard-depend on `/bin/bash`. 70 | 71 | ### Will this work on OpenVZ 6? 72 | 73 | No, because the kernel is too old to start systemd. 74 | Please do not buy such VPSes no matter how cheap they may be. 75 | -------------------------------------------------------------------------------- /installer.nix: -------------------------------------------------------------------------------- 1 | { lib, pkgs, config, ... }: 2 | let 3 | firstTimeInit = pkgs.writeShellScript "first-time-init" '' 4 | systemConfig=${config.system.build.toplevel} 5 | export PATH=${pkgs.coreutils}/bin 6 | 7 | lustrateRoot () { 8 | local root="$1" 9 | 10 | echo 11 | echo -e "\e[1;33m<<< NixOS is now lustrating the root filesystem (cruft goes to /old-root) >>>\e[0m" 12 | echo 13 | 14 | mkdir -m 0755 -p "$root/old-root.tmp" 15 | 16 | echo 17 | echo "Moving impurities out of the way:" 18 | for d in "$root"/* 19 | do 20 | [ "$d" == "$root/nix" ] && continue 21 | [ "$d" == "$root/boot" ] && continue # Don't render the system unbootable 22 | [ "$d" == "$root/old-root.tmp" ] && continue 23 | 24 | mv -v "$d" "$root/old-root.tmp" 25 | done 26 | 27 | # Use .tmp to make sure subsequent invokations don't clash 28 | mv -v "$root/old-root.tmp" "$root/old-root" 29 | 30 | mkdir -m 0755 -p "$root/etc" 31 | touch "$root/etc/NIXOS" 32 | 33 | exec 4< "$root/old-root/etc/NIXOS_LUSTRATE" 34 | 35 | echo 36 | echo "Restoring selected impurities:" 37 | while read -u 4 keeper; do 38 | dirname="$(dirname "$keeper")" 39 | mkdir -m 0755 -p "$root/$dirname" 40 | cp -av "$root/old-root/$keeper" "$root/$keeper" 41 | done 42 | 43 | exec 4>&- 44 | } 45 | 46 | if [[ -f "/etc/NIXOS_LUSTRATE" ]]; then 47 | lustrateRoot "/" 48 | fi 49 | 50 | exec $systemConfig/init 51 | ''; 52 | 53 | lustrateKeepFiles = pkgs.writeText "lustrate-keep-files" '' 54 | /nix-path-registration 55 | ''; 56 | 57 | fastboot = pkgs.writeText "fastboot" ""; 58 | in { 59 | system.build.tarball = pkgs.callPackage (pkgs.path + "/nixos/lib/make-system-tarball.nix") { 60 | extraArgs = "--owner=0"; 61 | 62 | storeContents = [ 63 | { 64 | object = config.system.build.toplevel; 65 | symlink = "none"; 66 | } 67 | { 68 | object = firstTimeInit; 69 | symlink = "/sbin/init"; 70 | } 71 | { 72 | object = config.system.build.binBashWrapper; 73 | symlink = "/bin/bash"; 74 | } 75 | { 76 | object = fastboot; 77 | symlink = "/fastboot"; 78 | } 79 | { 80 | object = lustrateKeepFiles; 81 | symlink = "/etc/NIXOS_LUSTRATE"; 82 | } 83 | ]; 84 | 85 | contents = [ 86 | #{ 87 | # source = "${config.system.build.toplevel}/init"; 88 | # target = "/sbin/init"; 89 | #} 90 | ]; 91 | 92 | extraCommands = "mkdir -p proc sys dev"; 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /nixos.nix: -------------------------------------------------------------------------------- 1 | { lib, pkgs, config, ... }: 2 | let 3 | # With most out-of-box templates, OpenVZ automatically runs a set of bash scripts 4 | # in the guest container every boot to customize the system (setting hostname, IP 5 | # addresses, etc.). We can't run them in NixOS, but they have to be successfully 6 | # executed for the container to boot :( 7 | # 8 | # Here we use an ugly hack to silently ignore the scripts. Note that `vzctl enter` 9 | # as well as the "Serial Console" feature in SolusVM also hard-depend on /bin/bash. 10 | binBashWrapper = pkgs.writeShellScript "bash" '' 11 | if [[ "$(${pkgs.coreutils}/bin/tr -d '\0'