diff --git a/lib/modules/valhalla.nix b/lib/modules/valhalla.nix
index f308fc6e..fd318baf 100644
--- a/lib/modules/valhalla.nix
+++ b/lib/modules/valhalla.nix
@@ -12,6 +12,20 @@
 
     options = {
       valhalla = {
+        setupUser = {
+          name = mkOption {
+            type = types.str;
+            description = "The name of the user used to set up the system.";
+            default = "heimdall";
+          };
+
+          id = mkOption {
+            type = types.int;
+            description = "The UID of the user used to set up the system.";
+            default = 420;
+          };
+        };
+
         timeZone = mkOption {
           type = types.nullOr types.str;
           description = "The time zone of the system.";
diff --git a/scripts/Arch/OS/setup.fish b/scripts/Arch/OS/setup.fish
index 2939c7f2..fe8fa0fe 100644
--- a/scripts/Arch/OS/setup.fish
+++ b/scripts/Arch/OS/setup.fish
@@ -1,98 +1,114 @@
 #!/bin/env fish
 begin
     set -l dir (status dirname)
-    source "$dir/../../Common/Scripts/config.fish"
-    source "$dir/../../Common/Scripts/hooks.fish"
-    source "$dir/../../Common/Scripts/wait-network.fish"
-
     set -q CONFIG_MODULE || set -l CONFIG_MODULE "$dir/config.nix"
+    source "$dir/../../Common/Scripts/config.fish"
 
     set -l mountDir (getConfig valhalla.partition.rootDir)
 
-    set -q USER_NAME || set -l USER_NAME manuel
-    set -q USER_DISPLAYNAME
-    set -q USER_GROUPS || set -l USER_GROUPS ""
+    function setupOS -V dir -V CONFIG_MODULE -V mountDir
+        source "$dir/../../Common/Scripts/hooks.fish"
+        source "$dir/../../Common/Scripts/wait-network.fish"
 
-    waitNetwork
-    and begin
-        set -l projectRoot (realpath "$dir/../../..")
-        set -l projectName (basename "$projectRoot")
-        set -l relativeDir (realpath --relative-to "$projectRoot" "$dir")
-        set -l tempRoot "/opt/$(basename "$projectName")"
-        set -l tempDir "$tempRoot/$relativeDir"
-
-        if set -l keyMap (getConfig valhalla.keyMap)
-            loadkeys "$keyMap"
-        end
-
-        and if set -l timezone (getConfig valhalla.timeZone)
-            timedatectl set-timezone "$timezone"
-        end
-
-        and CONFIG_MODULE=$CONFIG_MODULE bash "$dir/../../Common/OS/partition.sh"
-
-        and pacman-key --init
-        and pacman-key --populate
-
-        and pacstrap -K "$mountDir" \
-            base \
-            linux \
-            linux-firmware \
-            networkmanager \
-            man-db \
-            man-pages \
-            texinfo
-
-        and runHook installDrivers "Installing drivers…" || true
-
-        and "$dir/../../copy-repo.fish" "$mountDir$tempRoot"
-        and genfstab -U "$mountDir" >> "$mountDir/etc/fstab"
-
-        and arch-chroot "$mountDir" systemctl enable NetworkManager
-
-        and if set -q timezone
-            arch-chroot "$mountDir" ln -sf "/usr/share/zoneinfo/$timezone" /etc/localtime
-        end
-
-        and arch-chroot "$mountDir" hwclock --systohc
+        set -q USER_NAME || set -l USER_NAME manuel
+        set -q USER_DISPLAYNAME
+        set -q USER_GROUPS || set -l USER_GROUPS ""
 
+        waitNetwork
         and begin
-            getConfig valhalla.i18n.localeSettings --json | \
-                jq --raw-output '[.[] | split(".") | .[0]] | unique | join("\\\\|")'
-        end | begin
-            read LOCALES
-            and arch-chroot "$mountDir" sed -i "s/^#\?\(\($LOCALES\).*\)\$/\1/" /etc/locale.gen
-        end
+            set -l projectRoot (realpath "$dir/../../..")
+            set -l projectName (basename "$projectRoot")
+            set -l relativeDir (realpath --relative-to "$projectRoot" "$dir")
+            set -l tempRoot "/opt/$(basename "$projectName")"
+            set -l tempDir "$tempRoot/$relativeDir"
 
-        and begin
-            getConfig valhalla.i18n.localeSettings --json | \
-                jq --raw-output '[keys[] as $key | "\($key)=\(.[$key])"] | join("\n")'
-        end | arch-chroot "$mountDir" tee /etc/locale.conf > /dev/null
+            if set -l keyMap (getConfig valhalla.keyMap)
+                loadkeys "$keyMap"
+            end
 
-        and if set -q keyMap
-            echo "KEYMAP=$keyMap" | arch-chroot "$mountDir" tee /etc/vconsole.conf > /dev/null
-        end
+            and if set -l timezone (getConfig valhalla.timeZone)
+                timedatectl set-timezone "$timezone"
+            end
 
-        and echo "$ARCH_HOSTNAME" | arch-chroot "$mountDir" tee /etc/hostname > /dev/null
+            and CONFIG_MODULE=$CONFIG_MODULE bash "$dir/../../Common/OS/partition.sh"
 
-        and arch-chroot "$mountDir" mkinitcpio -P
-        and arch-chroot "$mountDir" bash "$tempDir/../Software/GRUB/install.sh"
-        and arch-chroot "$mountDir" bash "$tempDir/../Software/sudo/install.sh"
-        and pacstrap -K "$mountDir" git
+            and pacman-key --init
+            and pacman-key --populate
 
-        and USER_NAME="$USER_NAME" \
-        USER_DISPLAYNAME="$USER_DISPLAYNAME" \
-        USER_GROUPS="$USER_GROUPS" \
-            arch-chroot "$mountDir" bash "$tempDir/user.sh"
+            and pacstrap -K "$mountDir" \
+                base \
+                linux \
+                linux-firmware \
+                networkmanager \
+                man-db \
+                man-pages \
+                texinfo
 
-        and if set -l keyLayout (getConfig valhalla.keyboardLayout)
-            set -l serviceName set-keymap.service
-            and set -l serviceFile "$mountDir/etc/systemd/system/$serviceName"
-            and cp "$dir/$serviceName" "$serviceFile"
-            and systemd-nspawn -D "$mountDir" systemctl enable "$serviceName"
-            and systemd-nspawn -bD "$mountDir" -E "ARCH_X11_KEYMAP=$keyLayout"
-            and systemd-nspawn -D "$mountDir" systemctl disable "$serviceName"
-            and rm "$serviceFile"
+            and runHook installDrivers "Installing drivers…" || true
+
+            and "$dir/../../copy-repo.fish" "$mountDir$tempRoot"
+            and genfstab -U "$mountDir" >> "$mountDir/etc/fstab"
+
+            and arch-chroot "$mountDir" systemctl enable NetworkManager
+
+            and if set -q timezone
+                arch-chroot "$mountDir" ln -sf "/usr/share/zoneinfo/$timezone" /etc/localtime
+            end
+
+            and arch-chroot "$mountDir" hwclock --systohc
+
+            and begin
+                getConfig valhalla.i18n.localeSettings --json | \
+                    jq --raw-output '[.[] | split(".") | .[0]] | unique | join("\\\\|")'
+            end | begin
+                read LOCALES
+                and arch-chroot "$mountDir" sed -i "s/^#\?\(\($LOCALES\).*\)\$/\1/" /etc/locale.gen
+            end
+
+            and begin
+                getConfig valhalla.i18n.localeSettings --json | \
+                    jq --raw-output '[keys[] as $key | "\($key)=\(.[$key])"] | join("\n")'
+            end | arch-chroot "$mountDir" tee /etc/locale.conf > /dev/null
+
+            and if set -q keyMap
+                echo "KEYMAP=$keyMap" | arch-chroot "$mountDir" tee /etc/vconsole.conf > /dev/null
+            end
+
+            and echo "$ARCH_HOSTNAME" | arch-chroot "$mountDir" tee /etc/hostname > /dev/null
+
+            and arch-chroot "$mountDir" mkinitcpio -P
+            and arch-chroot "$mountDir" bash "$tempDir/../Software/GRUB/install.sh"
+            and arch-chroot "$mountDir" bash "$tempDir/../Software/sudo/install.sh"
+            and pacstrap -K "$mountDir" git
+
+            and USER_NAME="$USER_NAME" \
+            USER_DISPLAYNAME="$USER_DISPLAYNAME" \
+            USER_GROUPS="$USER_GROUPS" \
+                arch-chroot "$mountDir" bash "$tempDir/user.sh"
+
+            and if set -l keyLayout (getConfig valhalla.keyboardLayout)
+                set -l serviceName set-keymap.service
+                and set -l serviceFile "$mountDir/etc/systemd/system/$serviceName"
+                and cp "$dir/$serviceName" "$serviceFile"
+                and systemd-nspawn -D "$mountDir" systemctl enable "$serviceName"
+                and systemd-nspawn -bD "$mountDir" -E "ARCH_X11_KEYMAP=$keyLayout"
+                and systemd-nspawn -D "$mountDir" systemctl disable "$serviceName"
+                and rm "$serviceFile"
+            end
         end
     end
+
+    function autologin -S
+        set -l file "/etc/systemd/system/getty@tty1.service.d/autologin.conf"
+        arch-chroot "$mountDir" mkdir -p (dirname "$file")
+
+        begin
+            printf %s\n \
+                "[Service]" \
+                "ExecStart=" \
+                "ExecStart=-/sbin/agetty -o '-p -f -- \\u' --noclear --autologin root %I \$TERM"
+        end | arch-chroot "$mountDir" tee "$file" > /dev/null
+    end
+
+    source "$dir/../../Common/OS/setup.fish"
 end
diff --git a/scripts/Common/OS/setup.fish b/scripts/Common/OS/setup.fish
new file mode 100644
index 00000000..88f4e3d2
--- /dev/null
+++ b/scripts/Common/OS/setup.fish
@@ -0,0 +1,32 @@
+#!/bin/env fish
+begin
+    set -l dir (status dirname)
+    source "$dir/../Scripts/config.fish"
+    source "$dir/../Scripts/hooks.fish"
+    set -l name (getConfig valhalla.setupUser.name)
+    set -l mountDir (getConfig valhalla.partition.rootDir)
+    runHook setupOS || true
+
+    and runHook createUser || \
+        begin
+            chroot "$mountDir" \
+                useradd \
+                    --comment "PortValhalla setup user" \
+                    --system \
+                    --no-user-group \
+                    --groups wheel \
+                    --create-home \
+                    --uid (getConfig valhalla.setupUser.id --json) \
+                    "$name"
+        end
+
+    chroot "$mountDir" usermod -aG wheel "$name"
+
+    begin
+        echo "$name ALL=(ALL:ALL) NOPASSWD: ALL"
+    end | chroot "$mountDir" tee "/etc/sudoers.d/PortValhalla" > /dev/null
+
+    and runHook autologin || true
+    and echo "Setup finished!"
+    and echo "Please reboot your machine"
+end