Skip to content

Instantly share code, notes, and snippets.

@yorickdowne
Last active May 2, 2026 04:50
Show Gist options
  • Select an option

  • Save yorickdowne/3cecc7b424ce241b173510e36754af47 to your computer and use it in GitHub Desktop.

Select an option

Save yorickdowne/3cecc7b424ce241b173510e36754af47 to your computer and use it in GitHub Desktop.
Debian 13 trixie upgrade

Debian 13 "Trixie"

Caution On systems with more than one EFI partition, such as systems set up for RAID with mdadm, grub-efi only upgrades one of the EFI partitions, the one mounted to /boot/efi. This can cause failure on reboot, with the system going into BIOS or EFI shell. Discussion here: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1135137
See bottom of this gist for a way to verify EFI before rebooting, and upgrading the second copy so a reboot is safe

To start, read the official release notes.

If your install fits into "vanilla Debian plus maybe a handful of 3rd-party repos", then this guide for a simple upgrade to Debian 13 "trixie" from Debian 12 "bookworm" can be helpful. 3rd-party repos are handled with a find command.

If you are on a fork of Debian such as RasPI OS, use their instructions, not this gist.

Note upgrade is only supported from Debian 12 to Debian 13. If you are on Debian 11, upgrade to Debian 12 first. Then once on Debian 12, you can upgrade to Debian 13.

From Debian 13, you can upgrade to Debian 14 after its release in summer 2027.

This guide is only for the OS itself. Applications are as plentiful as sand on the beach, and they may all require additional steps. Plan for that.

  • Check free disk space

df -h

5 GiB free is a conservative amount. sudo apt clean and sudo apt autoremove can be used to free some disk space.

On a server with only docker installed, even 1 GiB free was sufficient. Do err on the side of caution here, however.

  • Identify any 3rd-party repos that may need to be updated. They'll be changed with a find command, below.

ls /etc/apt/sources.list.d

  • Update current distribution

sudo apt update && sudo apt full-upgrade

If this brought in a new kernel, sudo reboot - otherwise continue

  • Optional: Start a screen session

So that an SSH disconnect doesn't stop your upgrade halfway through, you can run in screen:

sudo apt install -y screen && screen
  • Change old-style Debian repos to trixie, from bookworm.

sources.list might no longer be in use. If your system uses debian.sources, the next step takes care of that

sudo sed -i 's/bookworm/trixie/g' /etc/apt/sources.list

non-free has been split since Debian 12 and if used, you should also use non-free-firmware. If you are unsure, check and adjust. More complete instructions are in the Debian release notes and the gist for Debian 12 upgrade

  • Change all repos in sources.list.d to trixie, from bookworm

This assumes the repos have trixie versions. Run sudo apt update after the change to trixie to confirm, and deal with any repos that aren't available in trixie.

sudo find /etc/apt/sources.list.d -type f \( -name '*.list' -o -name '*.sources' \) -exec sed -i 's/bookworm/trixie/g' {} \;

Run sudo apt update and if it fails on some repos because they don't have trixie versions, disable them for now, then test again.

  • Update Debian

For the following, say Yes to restarting services, and keep existing config files when prompted.

sudo apt update && sudo apt full-upgrade

  • If you use mdadm, stop and check EFI

Having more than one ESP (EFI System Partition) is an unusual setup. If you are certain you have only one ESP, move on to the next step.

In an mdadm two-disk setup with two EFI partitions, only one of the two EFI partitions is being updated to 2.12, the one mounted to /boot/efi. Note this is not the only way to configure an EFI partition: It might be a RAID1 setup itself, in which case there is no issue. I have seen this issue on legacy OVH Debian installations. It is not present on new installations

On reboot, if the ESP (EFI System Partition) still on 2.06 ends up as the primary boot EFI, you'll get dumped into BIOS or EFI shell. This is a "longstanding hole in grub-efi" according to maintainers. See top of this gist for a discussion link.

If you do end up in BIOS, launch EFI shell. Then look at fs0: and fs1:, and choose the one with the newer shimx64.efi (SecureBoot) or grubx64.efi (no SecureBoot), and execute that. The newer grubx64.efi will be around 2.6M in size, instead of around 4M.

To verify whether the second EFI partition was updated:

Run lsblk, see the partition mounted to /boot/efi, e.g. nvme0n1p1. There'll be a matching partition on the second drive, also vfat or fat32 or fat16, e.g. nvme1n1p1. Caution There is no guarantee that /boot/efi is mounted on the first drive, it might be mounted to any drive. Identify the EFI partitions on all drives, and inspect them. This example assumes two drives; adjust likewise for 3 and more drives.

Mount it

sudo mkdir -p /mnt/second-efi
sudo mount /dev/<second-efi-partition> /mnt/second-efi

Look at what the boot entry is called. It might be debian. Make a note.

ls /boot/efi/EFI

Install binutils sudo apt install binutils

Check EFI strings on both, assuming debian here, adjust to where yours actually is:

strings /boot/efi/EFI/debian/grubx64.efi | grep -i grub | tail -6
strings /mnt/second-efi/EFI/debian/grubx64.efi | grep -i grub | tail -6

If both show the same grub 2.12 entry, move on to reboot.

If one of them is old, continue here.

A healthy grub looked like this on my system:

grub_diskfilter_read_node
grub,5,Free Software Foundation,grub,2.12,https://www.gnu.org/software/grub/
grub.debian,5,Debian,grub2,2.12-9+deb13u1,https://tracker.debian.org/pkg/grub2
grub.debian13,1,Debian,grub2,2.12-9+deb13u1,https://tracker.debian.org/pkg/grub2
grub.peimage,2,Canonical,grub2,2.12-9+deb13u1,https://salsa.debian.org/grub-team/grub/-/blob/master/debian/patches/secure-boot/efi-use-peimage-shim.patch
&Debian Secure Boot Signer 2022 - grub20

An old grub that hadn't been updated looked like this on my system:

        source $prefix/grub.cfg
        source $cmdpath/grub.cfg
normal (memdisk)/grub.cfg
grub,4,Free Software Foundation,grub,2.06,https://www.gnu.org/software/grub/
grub.debian,4,Debian,grub2,2.06-13+deb12u1,https://tracker.debian.org/pkg/grub2
&Debian Secure Boot Signer 2022 - grub20

Be very sure where your grubx64.efi is. The next command will always succeed, but if the --bootloader-id is wrong, it'll just create another entry that never gets used. I am assuming debian here. The correct --bootloader-id is in /etc/default/grub as GRUB_DISTRIBUTOR. Also be careful of the target: This is for x64 Intel/AMD. Adjust for ARM64.
Also be very sure of which EFI directory has the old grub. It should be /mnt/second-efi, but verify and set --efi-directory to the old one.

case "$(dpkg --print-architecture)" in \
  amd64) efi_target="x86_64-efi" ;; \
  arm64) efi_target="arm64-efi" ;; \
  *) \
    echo "Unsupported architecture" \
    ;; \
esac
sudo grub-install --target=$efi_target --efi-directory=/mnt/second-efi --bootloader-id=debian --recheck

Verify once more on both. I am again assuming debian

strings /boot/efi/EFI/debian/grubx64.efi | grep -i grub | tail -6
strings /mnt/second-efi/EFI/debian/grubx64.efi | grep -i grub | tail -6

If both entries now show grub 2.12, you can move on to the next step

  • Reboot - If you use mdadm, only proceed if you know EFI is OK

sudo reboot

  • Clean up old repos

sudo apt autoremove && sudo apt clean

  • Modernize Debian sources

Optional but recommended: Switch to deb822 format for the sources.list. This will write /etc/apt/sources.list.d/debian.sources and /etc/apt/sources.list.d/debian-backports.sources

sudo apt modernize-sources

Caveat that trixie-backports might not have a Signed-By on some 3rd-party mirrors. You can fix this by manually setting Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg in /etc/apt/sources.list.d/debian-backports.sources

  • Clean up old /tmp

Optional: Remove files from the old /tmp directory. Debian 13 replaced it with a tmpfs, see https://www.debian.org/releases/trixie/release-notes/issues.en.html#the-temporary-files-directory-tmp-is-now-stored-in-a-tmpfs

On most systems /tmp will be empty or all-but. If you did store large files here, it can be worth the effort to clean up.

sudo mkdir /mnt/tmp-chk
sudo mount --bind / /mnt/tmp-chk
ls -lha /mnt/tmp-chk/tmp/

Remove files from /mnt/tmp-chk/tmp as you see fit, then unmount again

sudo umount /mnt/tmp-chk
sudo rm -rf /mnt/tmp-chk

Automated by Ansible

Caution: This Ansible playbook does not check EFI has been updated on all disks, on an mdadm setup. Only use on single-drive machines, or adjust this playbook to take care of upgrading all EFI partitions.

Config ansible.cfg:

[defaults]
interpreter_python = /usr/bin/python3

Playbook trixie.yml:

---
- name: Upgrade to Debian trixie
  hosts: all
  serial: 1
  gather_facts: false
  roles:
    - base/upgrade_trixie

Role base/upgrade_trixie/tasks/main.yml:

---
- name: Get distribution version
  setup:
    filter: ansible_distribution*
- name: Skip if not Debian 12
  meta: end_host
  when: ansible_distribution != 'Debian' or ansible_distribution_major_version != '12'
- name: apt clean
  apt:
    clean: yes
  become: yes
- name: Get filesystem facts
  setup:
    filter: ansible_mounts
- name: Fail if free space on / is below 5 GiB
  ansible.builtin.assert:
    that:
      - item.size_available > (5 * 1024 * 1024 * 1024)
    fail_msg: "Free disk space on {{ item.mount }} is below 5 GiB"
  loop: "{{ ansible_mounts }}"
  when: item.mount == "/"
- name: All apt packages up to date
  apt:
    upgrade: dist
    update_cache: yes
  become: yes
- name: apt autoremove
  apt:
    autoremove: yes
  become: yes
- name: apt clean
  apt:
    clean: yes
  become: yes
- name: Check if reboot required
  ansible.builtin.stat:
    path: /run/reboot-required
    get_checksum: no
  register: reboot_required_file
- name: Reboot if required
  ansible.builtin.reboot:
    msg: "Reboot initiated by Ansible"
    connect_timeout: 5
    reboot_timeout: 600
    pre_reboot_delay: 0
    post_reboot_delay: 60
    test_command: whoami
  when: reboot_required_file.stat.exists
  become: true
- name: Switch OS from bookworm to trixie
  ansible.builtin.replace:
    path: /etc/apt/sources.list
    regexp: 'bookworm'
    replace: 'trixie'
  become: yes
- name: Find all 3rd-party repos
  ansible.builtin.find:
    paths: /etc/apt/sources.list.d
    patterns: '*'
    recurse: no
  register: third_party_repos
- name: Switch 3rd-party repos from bookworm to trixie
  ansible.builtin.replace:
    path: "{{ item.path }}"
    regexp: 'bookworm'
    replace: 'trixie'
  loop: "{{ third_party_repos.files }}"
  loop_control:
    label: "{{ item.path }}"
  become: yes
- name: Use apt to move to trixie
  apt:
    upgrade: dist
    update_cache: yes
  become: yes
- name: Get distribution version
  setup:
    filter: ansible_distribution*
- name: Fail if not Debian 13
  assert:
    that:
      - ansible_distribution_major_version == '13'
    fail_msg: "Upgrade to Debian 13 failed"
- name: apt autoremove
  apt:
    autoremove: yes
  become: yes
- name: apt clean
  apt:
    clean: yes
  become: yes
- name: Reboot on trixie
  ansible.builtin.reboot:
    msg: "Reboot initiated by Ansible"
    connect_timeout: 5
    reboot_timeout: 600
    pre_reboot_delay: 0
    post_reboot_delay: 60
    test_command: whoami
  become: yes
- name: Modernize apt sources
  ansible.builtin.command:
    cmd: apt -y modernize-sources
  become: yes
- name: Pause for 5 minutes for staggered upgrades
  pause:
    minutes: 5
@yorickdowne
Copy link
Copy Markdown
Author

@pjl Good suggestion. I've added a section about that. On most systems, there won't be anything in /tmp - but some folk may have used it.

@vico93
Copy link
Copy Markdown

vico93 commented Nov 12, 2025

How is the procedure for Raspbian Pi OS (raspbian)?

@dpslwk
Copy link
Copy Markdown

dpslwk commented Nov 18, 2025

Suggest moving Optional: Start a screen session to the first steps as doing the install after changing the list files might not work

@vitor251093
Copy link
Copy Markdown

@ghostersk thanks a lot! I was starting to think I would need to install Debian 13 from scratch because of that damn error.

My upgrade is going smoothly after that.

@mkd2380
Copy link
Copy Markdown

mkd2380 commented Feb 23, 2026

can i comment the third-party sources during upgrade ? i have installed php and docker on my server (Debian12) , i fully update and upgrade the packages , i want to comment or mv the *.list file . is that possible ?

@yorickdowne
Copy link
Copy Markdown
Author

@mkd2380 You should adjust these .list files to use the trixie repo, if they have trixie releases. Then test that this worked with sudo apt update. Even better, use deb822 versions, .sources files. Fresh from your third-party repos or via sudo apt modernize-sources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment