diff --git a/README.md b/README.md
index 8b1b94ce1e30ef8daa85c98b7b15de9996a38c9c..6d3c4d3bd7d9cb97956bf711c4ee22dfe17fa318 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ Install these packages:
 ### Instructions
 
 All systems are set up the same way. For the first time setup in the Hetzner rescue system,
-run the provisioning script: `ansible-playbook playbooks/tasks/install-arch.yml -l $host`.
+run the provisioning script: `ansible-playbook playbooks/tasks/install_arch.yml -l $host`.
 The provisioning script configures a sane basic systemd with sshd. By design, it is NOT idempotent.
 After the provisioning script has run, it is safe to reboot.
 
diff --git a/host_vars/build.archlinux.org/misc b/host_vars/build.archlinux.org/misc
index a6f684209c9d3168cbb40e013a652330e1976d12..df072145c5f02691114a021c733c7fdf5936f681 100644
--- a/host_vars/build.archlinux.org/misc
+++ b/host_vars/build.archlinux.org/misc
@@ -1,10 +1,10 @@
 hostname: "build.archlinux.org"
-network_interface: "enp195s0"
-ipv4_address: "135.181.138.48"
+network_interface: "eno8303"
+ipv4_address: "65.21.37.204"
 ipv4_netmask: "/32"
-ipv6_address: "2a01:4f9:3a:120f::2"
-ipv6_netmask: "/128"
-ipv4_gateway: "135.181.138.1"
+ipv6_address: "2a01:4f9:3a:458a::2"
+ipv6_netmask: "/64"
+ipv4_gateway: "65.21.37.193"
 ipv6_gateway: "fe80::1"
 filesystem: btrfs
 system_disks:
diff --git a/playbooks/build.archlinux.org.yml b/playbooks/build.archlinux.org.yml
index 929ec409be04b5fc20216909f9636ec15b9f949b..c22544e49afa2eecd4a2aa83ded3dd1d132f489f 100644
--- a/playbooks/build.archlinux.org.yml
+++ b/playbooks/build.archlinux.org.yml
@@ -4,6 +4,7 @@
   roles:
     - { role: common }
     - { role: tools, extra_utils: ['setconf', 'mlocate'] }
+    - { role: firewalld }
     - { role: sshd }
     - { role: root_ssh }
     - { role: archusers }
@@ -11,6 +12,6 @@
     - { role: mirrorsync }
     - { role: archbuild }
     - { role: fail2ban }
+    - { role: wireguard }
     - { role: prometheus_exporters }
     - { role: promtail }
-    - { role: wireguard }
diff --git a/roles/install_arch/tasks/main.yml b/roles/install_arch/tasks/main.yml
index 0bfbd9c8ce58939453e6e686812db26dae602582..1949af4000a51852461208bdf932f2a192c8a592 100644
--- a/roles/install_arch/tasks/main.yml
+++ b/roles/install_arch/tasks/main.yml
@@ -36,25 +36,41 @@
   register: sgdisk
   changed_when: "sgdisk.rc == 0"
 
+- name: Create EFI partitions
+  command: sgdisk -g -n 2:0:+512M {{ item }} -c 2:efi -t 2:ef00
+  with_items:
+    - "{{ system_disks }}"
+  register: sgdisk
+  changed_when: "sgdisk.rc == 0"
+
 - name: Create root partitions
-  command: sgdisk -n 2:0:0 {{ item }} -c 2:root
+  command: sgdisk -n 3:0:0 {{ item }} -c 3:root
   with_items:
     - "{{ system_disks }}"
   register: sgdisk
   changed_when: "sgdisk.rc == 0"
 
+- name: Format the efi disk  # noqa no-changed-when
+  command: mkfs.fat -F 32 -S 4096 -n efi {{ system_disks[0] }}{{ 'p2' if 'nvme' in system_disks[0] else '2' }}
+
 - name: Partition and format the disks (btrfs RAID)  # noqa no-changed-when
-  command: mkfs.btrfs -f -L root -d {{ raid_level | default('raid1') }} -m {{ raid_level | default('raid1') }} -O no-holes {{ system_disks | map('regex_replace', '^(.*)$', '\g<1>p2' if 'nvme' in system_disks[0] else '\g<1>2') | join(' ') }}
+  command: mkfs.btrfs -f -L root -d {{ raid_level | default('raid1') }} -m {{ raid_level | default('raid1') }} -O no-holes {{ system_disks | map('regex_replace', '^(.*)$', '\g<1>p3' if 'nvme' in system_disks[0] else '\g<1>3') | join(' ') }}
   when: filesystem == "btrfs" and system_disks | length >= 2
 
 - name: Partition and format the disks (btrfs single)  # noqa no-changed-when
-  command: mkfs.btrfs -f -L root -d single -m single -O no-holes {{ system_disks[0] }}{{ 'p2' if 'nvme' in system_disks[0] else '2' }}
+  command: mkfs.btrfs -f -L root -d single -m single -O no-holes {{ system_disks[0] }}{{ 'p3' if 'nvme' in system_disks[0] else '3' }}
   when: filesystem == "btrfs" and system_disks | length == 1
 
 - name: Mount the filesystem (btrfs)
-  mount: src="{{ system_disks[0] }}{{ 'p2' if 'nvme' in system_disks[0] else '2' }}" path=/mnt state=mounted fstype=btrfs opts="compress-force=zstd,space_cache=v2"
+  mount: src="{{ system_disks[0] }}{{ 'p3' if 'nvme' in system_disks[0] else '3' }}" path=/mnt state=mounted fstype=btrfs opts="compress-force=zstd,space_cache=v2"
   when: filesystem == "btrfs"
 
+- name: Create the efi mountpoint
+  file: path=/mnt/efi state=directory mode='0755'
+
+- name: Mount the efi filesystem
+  mount: src="{{ system_disks[0] }}{{ 'p2' if 'nvme' in system_disks[0] else '2' }}" path=/mnt/efi state=mounted fstype=vfat
+
 - name: Touch LOCK file on mountpoint
   file: path=/mnt/LOCK state=touch owner=root group=root mode=0644
 
@@ -161,12 +177,19 @@
     line: "GRUB_CMDLINE_LINUX_DEFAULT=\"rootflags=compress-force=zstd\""
   when: filesystem == "btrfs"
 
-- name: Install grub
+- name: Install grub (legacy mode)
   command: chroot /mnt grub-install --target=i386-pc --recheck {{ item }}
   with_items:
     - "{{ system_disks }}"
-  register: chroot_grub_install
-  changed_when: "chroot_grub_install.rc == 0"
+  register: chroot_grub_install_legacy
+  changed_when: "chroot_grub_install_legacy.rc == 0"
+
+- name: Install grub (uefi mode)
+  command: chroot /mnt grub-install --target=x86_64-efi --efi-directory=/efi --removable --recheck {{ item }}
+  with_items:
+    - "{{ system_disks }}"
+  register: chroot_grub_install_uefi
+  changed_when: "chroot_grub_install_uefi.rc == 0"
 
 - name: Configure grub
   command: chroot /mnt grub-mkconfig -o /boot/grub/grub.cfg
diff --git a/roles/sshd/templates/motd.j2 b/roles/sshd/templates/motd.j2
index 32dadb3a30a58c83735dc0052c5ca2c69742c78e..3871821bb65fb471baf829ef40bd813728cb7198 100644
--- a/roles/sshd/templates/motd.j2
+++ b/roles/sshd/templates/motd.j2
@@ -14,9 +14,9 @@ Data on this system is regularly backed up automatically.
   - SRCDEST is set to /var/lib/archbuilddest/srcdest (~srcdest).
   - Take a look at mkpkg -hh if you want to build nightlies.
 
-January 26, 2021
-  The common Arch build server is now build.archlinux.org! This one has 64 threads and
-  256GiB of memory. Now go and compile something.
+January 19, 2024
+  The common Arch build server is now build.archlinux.org! This one has 96 threads and
+  384GiB of memory. Now go and compile something.
 {% endif %}
 
 {% if 'public_html' in group_names %}
diff --git a/tf-stage1/archlinux.tf b/tf-stage1/archlinux.tf
index 9043186feba9eaa96631197312ae559102d12f6c..9177449749ca314d18e3f90d69c63c515bf62d31 100644
--- a/tf-stage1/archlinux.tf
+++ b/tf-stage1/archlinux.tf
@@ -255,8 +255,8 @@ locals {
   # }
   archlinux_org_a_aaaa = {
     build = {
-      ipv4_address = "135.181.138.48"
-      ipv6_address = "2a01:4f9:3a:120f::2"
+      ipv4_address = "65.21.37.204"
+      ipv6_address = "2a01:4f9:3a:458a::2"
     }
     gemini = {
       ipv4_address = "49.12.124.107"