staging.inyokaproject.org

Neueste Artikel

heute

Vor längerem habe ich diverse lokale Kalenderprogramme getestet. Unter anderem Kalendar und Merkuro. Damit soll per CalDAV auf einen Nextcloud-Kalender zugegriffen werden. Nach einigen Tests und irgendwelchen Änderungen konnte ich irgendwann unter Merkuro keine neuen Termine mehr anlegen. Jeder Versuch hat zur Fehlermeldung “Invalid parent collection” geführt. Alle Lösungen, die ich über eine Suchmaschine gefunden habe, hatten nicht funktioniert. Die Lösung in meinem Fall hat mich einige Zeit gekostet und war “interessant”.

Was ich alles probiert hatte was nicht funktioniert hat, werde ich nicht erzählen. Zum einen, weil es zu viel wäre. Aber auch, weil ich mich nicht mehr an alles erinnern kann. Ich werde daher nur beschreiben was schlussendlich zur Lösung geführt hat.

Erst mal habe ich mir die Konfigurationsdatei von Merkuro angesehen.

[GlobalCollectionSelection]
Current=x-1
Selection=c50

Nicht besonders aussagekräftig aber auf den ersten Blick auch nicht fehlerhaft. Irgendwann bin ich dann auf die Idee gekommen, mir die Konfigurationsdatei von Kalendar anzusehen, weil damit das Anlegen von Terminen funktioniert.

[Editor]
lastUsedEventCollection=41

[General]
lastOpenedView=MonthView

Hierbei ist mir aufgefallen, dass die Nummer der Selection bzw. Collection in den beiden Konfigurationsdateien unterschiedlich ist. Da mir nichts mehr eingefallen ist, wollte ich die Nummer aus der Konfigurationsdatei von Merkuro löschen. Allerdings habe ich versehentlich die falsche Konfigurationsdatei erwischt und lastUsedEventCollection=41 gelöscht. Als ich dann unter Merkuro einen neuen Termin anlegen wollte, habe ich keine Fehlermeldung mehr erhalten. Der Termin wurde aber nicht eingetragen, da scheinbar der Button “Eintragen” nun klickbar aber funktionslos war. Als ich meinen Irrtum bemerkt hatte und die Zeile wieder in die Konfigurationsdatei ~/.config/kalendarrc eingetragen hatte, hat der Button wieder funktioniert und besagte Fehlermeldung erzeugt. Was zur Hölle? Warum wirkt sich ein Eintrag in der Konfigurationsdatei von Kalendar auf Merkuro aus?

Mehrere vergebliche Versuche später.

Ich war inzwischen so weit, mir die Datenbank von Akonadi direkt anzusehen. Weil mir die unterschiedlichen Nummern immer noch keine Ruhe lassen. Was mich schlussendlich zur Lösung geführt hat.

mariadb \
  --socket=/run/user/$(id -u)/akonadi/mysql.socket \
  -D akonadi \
  -e 'SELECT * FROM resourcetable;'
+----+---------------------------------+-----------+
| id | name                            | isVirtual |
+----+---------------------------------+-----------+
|  1 | akonadi_search_resource         |         1 |
|  3 | akonadi_maildir_resource_0      |         0 |
|  4 | akonadi_contacts_resource_0     |         0 |
| 11 | akonadi_davgroupware_resource_4 |         0 |
+----+---------------------------------+-----------+

Mit dieser Datenbankabfrage habe ich mir den Namen der vorhandenen Ressourcen in der Akonadi-Datenbank anzeigen lassen. In meinem Fall betrifft es die Ressource mit der id 11.

mariadb --auto-vertical-output --socket=/run/user/$(id -u)/akonadi/mysql.socket -D akonadi -e "
SELECT
    c.id,
    c.name,
    c.parentId,
    c.resourceId,
    c.remoteId,
    r.name AS resource
FROM collectiontable c
LEFT JOIN resourcetable r ON r.id = c.resourceId
WHERE r.name = 'akonadi_davgroupware_resource_4'
ORDER BY c.id;
"
*************************** 1. row ***************************
        id: 49
      name: akonadi_davgroupware_resource_4
  parentId: NULL
resourceId: 11
  remoteId: akonadi_davgroupware_resource_4
  resource: akonadi_davgroupware_resource_4
*************************** 2. row ***************************
        id: 50
      name: https://example.com/remote.php/dav/calendars/Fryboyter/Kalendername/
  parentId: 49
resourceId: 11
  remoteId: https://example.com/remote.php/dav/calendars/Fryboyter/Kalendername/
  resource: akonadi_davgroupware_resource_4

Mit diesem Befehl habe ich mir anzeigen lassen, was alles mit dieser Ressource verknüpft ist. Was in dem Fall mein Nextcloud-Kalender mit der id 50 ist. Die Konfigurationsdatei von Merkuro müsste also korrekt sein. Was ist aber Collection 41 die in der anderen Konfigurationsdatei genannt wird?

mariadb \
  --socket=/run/user/$(id -u)/akonadi/mysql.socket \
  -D akonadi \
  -e "
SELECT
    id,
    name,
    parentId,
    resourceId,
    remoteId
FROM collectiontable
WHERE id = 41;
"

Der Befehl zeigt nichts an. Es gibt keine Collection mit der id 41.

Da ich nichts zu verlieren habe, habe ich in der Datei ~/.config/kalendarrc die Zeile lastUsedEventCollection=41 auf lastUsedEventCollection=50 geändert.

Und schon konnte ich wieder neue Termine in Merkuro anlegen.

Wie es zu den unterschiedlichen ID-Nummern gekommen ist, kann ich nicht sagen. Warum Merkuro scheinbar auch, zumindest teilweise, die Konfigurationsdatei von Kalendar berücksichtigt, entzieht sich auch meiner Kenntnis. Allerdings vermute ich, dass es damit zusammenhängt, dass Merkuro auf Kalendar basiert.

gestern

Mozilla hat Firefox 157 für Windows, Apple macOS und Linux veröffentlicht. Die neue Version bringt unter anderem das neue Nova-Design. Dieser Artikel fasst die wichtigsten Neuerungen zusammen.

Download Mozilla Firefox für Microsoft Windows, Apple macOS und Linux

Firefox hat ein neues Design – „Nova”

Firefox 157 hat eine neue Optik. Diese wurde intern unter dem Projektnamen „Nova” entwickelt. Charakteristisch für das neue Design von Firefox sind vor allem die starken Rundungen. Auch zahlreiche Icons wurden erneuert. Wo bislang Grau oder Blau als Akzentfarbe eingesetzt worden ist, setzt Mozilla nun auf Violett-Töne. Und während Flächen bislang einfarbig und graue Farben dominant waren, setzt Mozilla mit „Nova” auf mehr Farbe und teilweise auch auf dezente Verläufe. Dabei hat der Nutzer standardmäßig die Auswahl zwischen jeweils zwölf hellen sowie dunklen Farbstimmungen, welche sich jetzt auch direkt über die Startseite auswählen lassen, deren Hintergrundfarbe sich nun auch an das Firefox-Theme anpasst.

Das „Nova”-Design von Firefox habe ich in einem separaten Artikel ausführlich behandelt.

Firefox 157 Firefox 157

Neue Option für helles vs. dunkles Theme

Überall, wo die Themes zur Auswahl stehen, kann der Nutzer jetzt auch direkt auswählen, ob er ein helles oder dunkles Theme nutzen möchte, was Auswirkungen auf Teile der Oberfläche wie Menü, Panels und die Startseite hat.

Firefox 157

Neue Sidebar für alle Nutzer aktiviert

Die neue Sidebar-Implementierung, welche bisher über die Einstellungen aktiviert werden konnte, ist nun für alle Nutzer standardmäßig aktiviert. Die alte Sidebar-Implementierung kann weiterhin über about:config aktiviert werden, indem die Option sidebar.revamp per Doppelklick auf false gesetzt wird. Die versteckte Option soll bis Ende 2027 verfügbar bleiben.

Neue Einstellung für Dichte der Oberfläche

Eine neue Einstellung zur Dichte erlaubt es, den Abstand rund um Fensterelemente wie Symbolleiste, Tabs und Seitenleiste anzupassen. Während dies in den letzten Jahren nur über eine versteckte Option möglich war und nicht mehr offiziell unterstützt wurde, handelt es sich jetzt wieder um eine offiziell unterstützte Konfiguration, die sich in den Einstellungen unter „Erscheinungsbild” vornehmen lässt.

Neben dem automatischen Modus, der eine entsprechende Systemeinstellung berücksichtigt, stehen die Optionen Standard, Kompakt sowie Touch zur Verfügung. Bei kompakter Darstellung werden Elemente platzsparender angeordnet, während die Touch-Option für größere Flächen sorgt.

Firefox 157

Sonstige Neuerungen von Firefox 157

Kontextbezogene Vorschläge von Firefox in der Adressleiste stehen ab sofort auch in den Ländern Österreich, Schweiz, Belgien, Tschechien, Dänkemark, Finland, Ungarn, Irland, Luxemburg, Niederlande, Norwegen, Polen, Portugal, Slowakei, Spanien und Schweden zur Verfügung.

Firefox zeigt eine Warnung im Panel für die Website-Informationen sowie in den Einstellungen an, wenn die Umgebungsvariable SSLKEYLOGFILE auf dem System vorhanden ist, was die TLS-Schlüsselprotokollierung in Firefox aktiviert, wodurch andere Programme auf dem Computer den verschlüsselten Webdatenverkehr auslesen könnten. Dies ist eine Konfiguration, wie sie teilweise von sogenannter Sicherheits-Software vorgenommen wird.

Amazon steht nicht länger standardmäßig als Suchmaschine zur Verfügung, kann aber natürlich weiterhin manuell vom Benutzer installiert werden.

Video-Telefonie via WebRTC kann nun auch ein durch die Hardware beschleunigtes Decoding von AV1 auf Geräten mit entsprechender Unterstützung nutzen.

Die JavaScript-Dialoge alert(), confirm() und prompt(), die Farb- und Datumsauswahl sowie die Dropdown-Menüs zur automatischen Formularvervollständigung passen sich nun dem hellen oder dunklen Farbschema der Website an.

Auch in Firefox 157 wurden wieder mehrere Sicherheitslücken geschlossen. Alleine aus Gründen der Sicherheit ist ein Update auf Firefox 157 daher für alle Nutzer dringend empfohlen.

Darüber hinaus gab es diverse Korrekturen und Optimierungen, welche in den offiziellen Release Notes näher beschrieben werden, darunter eine verbesserte Synchronisierung von Audio und Video bei wiederholter Änderung der Wiedergabegeschwindigkeit von Videos.

Verbesserungen der Webplattform lassen sich in den MDN web docs nachlesen.

Der Beitrag Mozilla veröffentlicht Firefox 157 mit Nova-Design erschien zuerst auf soeren-hentzschel.at.

28. September 2026

Mozilla stellt dem Mozilla Data Collective weitere fünf Millionen USD zur Verfügung. Die Datenplattform will damit ihr Angebot an Datensätzen für die Entwicklung von Künstlicher Intelligenz ausbauen.

Datensätze für mehr als 450 Sprachen

Über das Mozilla Data Collective können Organisationen und Einzelpersonen Datensätze zur Verfügung stellen, ohne die Kontrolle darüber abzugeben. Sie entscheiden selbst, wer die Daten unter welchen Bedingungen nutzen darf und ob sie dafür eine Vergütung verlangen. Auch die Datensätze von Mozilla Common Voice werden über die Plattform angeboten.

Seit dem Start ist das Angebot des Mozilla Data Collective auf über 1.700 Datensätze für mehr als 450 Sprachen gewachsen. Rund 350 Organisationen wurden als Anbieter zugelassen.

Nach Angaben von Mozilla nutzen bereits große KI-Labore, Tausende KI-Startups und wachsende Unternehmen sowie Dutzende Unternehmen mit einer Bewertung von mindestens einer Milliarde Dollar Datensätze der Plattform. Auch wirtschaftlich entwickle sich das Mozilla Data Collective schneller als erwartet: Der auf ein Jahr hochgerechnete Umsatz liege beim Neunfachen des für diese Wachstumsphase gesetzten Ziels.

Weitere Investition für neue Datensätze und Funktionen

Im Mai 2026 erklärte die Mozilla Foundation, bis zu zehn Millionen USD für die Entwicklung des Mozilla Data Collective zugesagt zu haben. Mit den nun angekündigten zusätzlichen fünf Millionen USD möchte Mozilla Data Collective unter anderem kulturelle Videodatensätze sowie größere Textsammlungen für europäische, afrikanische und südasiatische Sprachen erschließen. Geplant sind außerdem neue Lizenzmodelle und günstigere Abonnements für Startups sowie Funktionen, mit denen Organisationen größere Datenbestände kontrollierter bereitstellen können.

Dabei sieht Mozilla auch in der Regulierung einen wichtigen Faktor: Vorschriften wie der AI Act der Europäischen Union lenken mehr Aufmerksamkeit auf die Herkunft von KI-Trainingsdaten sowie auf Lizenzen und die Zustimmung derjenigen, die Daten bereitstellen. Auf diese Punkte legt Mozilla Data Collective bei der Prüfung und Bereitstellung seiner Datensätze bereits Wert.

Der Beitrag Mozilla investiert weitere fünf Millionen USD in Mozilla Data Collective erschien zuerst auf soeren-hentzschel.at.

27. September 2026

Mozilla und das kanadische KI-Forschungsinstitut Mila haben eine neue Initiative für vertrauenswürdige Open-Source-KI angekündigt. Mit Unterstützung der kanadischen Regierung soll eine offene KI-Grundlage entstehen, die Organisationen lokal betreiben und vollständig kontrollieren können.

Open-Source-KI einfacher in Betrieb nehmen

Bereits im März hatten Mozilla und Mila eine strategische Partnerschaft angekündigt, um offene und souveräne KI voranzubringen. Nun bauen die beiden Organisationen ihre Zusammenarbeit aus. Auf der kanadischen KI- und Technologiekonferenz ALL IN wurde eine neue gemeinsame Initiative vorgestellt.

Open-Source-Modelle stehen zwar längst zur Verfügung. Ein solches Modell sicher, zuverlässig und in bestehende Systeme integriert produktiv einzusetzen, erfordert aber häufig ein spezialisiertes Entwicklerteam und mehrere Monate Arbeit. Genau an dieser Stelle setzt die Initiative an: Unternehmen und andere Organisationen sollen ein einsatzbereites KI-Paket erhalten, das sie privat und unter eigener Kontrolle betreiben können. Damit sollen sie weder selbst ein komplexes Gesamtsystem entwickeln noch vollständig von proprietären KI-Diensten abhängig sein, bei denen jede Anfrage Kosten verursacht.

Offener Standard und Referenz-Implementierung

Die geplante Grundlage besteht aus zwei Teilen. Ein offener Standard soll in Form klar definierter Schnittstellen veröffentlicht werden. Dadurch sollen sich einzelne Bestandteile des KI-Systems jederzeit gegen bessere Alternativen austauschen lassen.

Dazu kommt eine funktionierende Referenz-Implementierung, die Organisationen auf eigener oder selbst gewählter Infrastruktur installieren können. Dabei sollen sie selbst entscheiden können, welche Modelle und Kontrollmechanismen sie einsetzen und mit welchen eigenen Daten das System arbeitet. Funktionen für die Verwaltung des Systems und von Zugriffsrechten sollen von Anfang an integriert sein.

Als mögliches Einsatzszenario nennt Mozilla einen kleineren Produktionsbetrieb. Ein privater KI-Assistent könnte dort Handbücher, Arbeitsabläufe und frühere Projektdateien durchsuchen, beim Verfassen von Berichten helfen, technische Fragen beantworten oder die Softwareentwicklung unterstützen. Da das System lokal betrieben werden kann, bleiben vertrauliche Unternehmensdaten in der eigenen Umgebung. Neben Unternehmen richtet sich das Projekt unter anderem auch an Krankenhäuser, gemeinnützige Organisationen und Behörden.

Sechs Millionen Dollar als Anschubfinanzierung

Mila übernimmt die technische Umsetzung und Koordination des Projekts, während Mozilla seine technische Expertise einbringt und fünf Millionen Dollar als Anschubfinanzierung bereitstellt. Der kanadische Hardware-Anbieter Hypertec investiert im ersten Jahr eine weitere Million Dollar, um erste Installationen bei kanadischen Unternehmen und Institutionen zu beschleunigen. Unterstützt wird die Initiative außerdem von der kanadischen Regierung. Weitere Unternehmen, Forschungseinrichtungen, Geldgeber, Regierungen und Entwickler sind eingeladen, sich an dem Projekt zu beteiligen.

An der Architektur und der Auswahl geeigneter Open-Source-Komponenten arbeiten Mozilla und Mila bereits seit sechs Monaten. Innerhalb der kommenden sechs Monate sollen erste funktionierende Referenz-Implementierungen für Unternehmen, Behörden und gemeinwohlorientierte Anwendungsfälle veröffentlicht werden. Das weiter gefasste Ziel für die kommenden zwei Jahre ist es, Open-Source-KI so einfach einsetzbar zu machen, dass sie überall und von jedem genutzt werden kann.

Dass hier noch eine Lücke besteht, zeigt Mozillas „State of Open Source AI“-Bericht: Zwar setzen 79 Prozent der Entwickler, die KI-Funktionen integrieren, auf offene Modelle. In den produktiven Einsatz schaffen es jedoch nur 53 Prozent der Teams. Als größte Hürden gelten Kosten, Sicherheit, Integration und Wartung.

Der Beitrag Mozilla und Mila starten neue Open-Source KI-Initiative erschien zuerst auf soeren-hentzschel.at.

What is BTRFS

Because I am currently intensively dealing with BTRFS and doing some research to shed light on this topic for myself, here is a list of my findings, explanations, and instructions. If you have any comments on this, you can find me in the Fediverse (About page). Otherwise, as always, I hope that this article helps one or another person to gain more clarity on the topic as well. And … write more blog articles yourself!

BTRFS stands for B-Tree File System and is a modern copy-on-write file system that was originally developed in 2007 by Chris Mason at Oracle and added to the Linux kernel 2.6.29 in 2009. It is now supported by various distributions and companies, including Debian, SUSE, Red Hat, Fujitsu, and Intel.

The essential difference from classic file systems such as ext4 or NTFS is that BTRFS does not overwrite data. When a file is changed, BTRFS writes the new version to a different location while the old version is retained until it is no longer needed.

This gives rise to several features:

  • Snapshots: Point-in-time captures of the file system that initially take up hardly any additional storage space.
  • Data integrity: BTRFS calculates checksums for data and metadata. Errors caused by faulty RAM or aging storage media are detected and can be corrected if redundancy is available (e.g., RAID1).
  • Subvolumes: The file system can be divided into logical parts, similar to partitions, but more flexible.
  • Transparent compression: Data can be compressed on the fly (e.g., with zstd).
  • Integrated RAID support for RAID 0, 1, 10, 5, and 6.

What Are Snapshots

A snapshot is a point-in-time capture of the file system. The current state of a folder is recorded and can be restored later.

Thanks to copy-on-write, a fresh snapshot initially takes up hardly any additional storage space. It points to the same data blocks as the original. Only when changes are made do new blocks come into existence while the old version is retained in the snapshot. The additional space consumption therefore corresponds to the difference between the original and the snapshot.

This gives rise to typical use cases:

  • Before system updates: Create a snapshot, perform the update, roll back if there are problems.
  • Automatic backups: Tools such as Snapper can create snapshots at fixed intervals and automatically delete old ones.
  • Experiments: Changes can be tested and, if necessary, completely undone.

Important: A snapshot is not a backup. In the event of a hardware failure, both the original and the snapshot are lost. Snapshots protect against logical errors such as faulty updates or accidental deletion, not against hardware failures. For real backups, snapshots are transferred to another medium using btrfs send and btrfs receive.

Notes

Snapshots are not a substitute for backups. A few points should be noted:

  • Space consumption: Old snapshots can take up a lot of space over time because they hold the differences from later changes. Automatic cleanup is therefore sensible.
  • Performance: With very large amounts of data or databases, copy-on-write can slow things down. For such cases, files or subvolumes can be marked with the nodatacow attribute.
  • Kernel and bootloader: If /boot is included in the snapshot, a rollback can become problematic if the kernel and modules no longer match. Many distributions therefore exclude /boot or handle the kernel separately.

Snapshots with btrfs “tool”

Snapshots can be created and managed entirely without additional tools such as Snapper or Timeshift. The necessary commands are part of btrfs-progs, which is present on every system with Btrfs support.

Creating a Snapshot

A snapshot is created with btrfs subvolume snapshot. The command requires a source subvolume and a target directory for the snapshot.

Read-only snapshot (recommended for backups):

sudo btrfs subvolume snapshot -r /home /snapshots/home/home_snapshot_$(date +%Y%m%d_%H%M%S)

The -r option creates a read-only snapshot. This cannot be changed accidentally and is suitable for later transfer with btrfs send.

Writable snapshot (for tests or clones):

sudo btrfs subvolume snapshot /home /snapshots/home/home_snapshot_ro

Without -r, a writable snapshot is created, which can be used like a complete copy of the file system.

Important: Snapshots can only be created from subvolumes, not from normal directories. If the source is not a subvolume, Btrfs returns an error. The target directory /snapshots/home/ must be located on its own subvolume or outside the source subvolume so that no recursive structure is created.

Listing Snapshots

All subvolumes and snapshots can be displayed with the following command:

sudo btrfs subvolume list /

The output shows, among other things, the ID, the parent ID, and the path of each subvolume. With the -s option, only snapshots are listed; with -r, only read-only ones.

A typical output of sudo btrfs subvolume list / looks like this:

ID 256 gen 12345 top level 5 path @
ID 257 gen 12340 top level 5 path @home
ID 258 gen 12200 top level 5 path @snapshots
ID 259 gen 12050 top level 5 path snapshots/home/home_snapshot_2026-03-14
ID 260 gen 11980 top level 5 path snapshots/home/home_snapshot_ro

The columns in detail:

  • ID: The unique number of the subvolume. This is used internally by Btrfs and is not to be confused with Snapper’s snapshot number.
  • gen: The generation, i.e., a counter that is incremented with every transaction in the file system. A higher value means that the subvolume was last changed later.
  • top level: The ID of the parent subvolume. The value 5 stands for the top-level subvolume, i.e., the root of the Btrfs file system.
  • path: The path of the subvolume relative to the top-level subvolume. This shows where the subvolume is located in the tree.

In this example, one can see:

  • @ is the root subvolume (ID 256).
  • @home is a separate subvolume for /home (ID 257).
  • @snapshots is its own subvolume (ID 258).
  • snapshots/home/home_snapshot_2026-03-14 and snapshots/home/home_snapshot_ro are the snapshots that were created under the path /snapshots/home/.

Note: The output shows the paths relative to the top-level subvolume, not relative to the mounted root. If /snapshots is mounted as a separate subvolume, the snapshot path appears accordingly under snapshots/home/.... If /snapshots is not mounted separately, the snapshots are located within the root subvolume and appear as @/snapshots/home/....

With the -s option, only snapshots are displayed:

sudo btrfs subvolume list -s /

With the -r option, only read-only subvolumes:

sudo btrfs subvolume list -r /

With the -t option, the type of subvolume (snapshot or subvolume) is also output:

sudo btrfs subvolume list -t /

Deleting a Snapshot

A snapshot is not removed with rm -rf, but with:

sudo btrfs subvolume delete /snapshots/home/home_snapshot_ro

The corresponding directory is removed immediately, but the actual data blocks are deleted in the background. The command returns immediately without waiting for the data deletion to complete.

If you want to wait until the deletion is fully complete:

sudo btrfs subvolume sync /snapshots/home

Rolling Back a Snapshot

To roll back to a snapshot, the current subvolume is moved and the snapshot is copied into its place:

## Rename the current subvolume as a backup
sudo mv /mnt/btrfs_root/@home /mnt/btrfs_root/@home_backup_$(date +%Y%m%d)

## Copy the snapshot as the new subvolume
sudo btrfs subvolume snapshot \
  /snapshots/home/home_snapshot_20260301 \
  /mnt/btrfs_root/@home

After mounting again, the restored state is available. The old state remains under @home_backup_... until it is manually deleted.

Backing Up Snapshots with send/receive

For real backups outside the system, btrfs send and btrfs receive are used. Both operations require read-only snapshots.

Initial backup:

sudo btrfs send /snapshots/home/home_snapshot_ro | btrfs receive /backupvol

Incremental backup (only changes since the last snapshot):

sudo btrfs send -p /snapshots/home/home_snapshot_ro /snapshots/home/home_snapshot_20260314 | btrfs receive /backupvol

The -p option specifies the previous snapshot as the parent. Only the differences between the two snapshots are transferred.

Creating, Configuring, and Using @snapshots

The storage location for snapshots is defined in the Btrfs subvolume layout and in /etc/fstab, not in Snapper’s configuration file. In Snapper’s configuration, SUBVOLUME merely specifies for which directory snapshots should be created (e.g., /). Whether the snapshots remain in the same subvolume or are relocated is decided by the configuration via the Btrfs subvolume structure and the mount entries in /etc/fstab.

In the Snapper configuration, reference is made to the @snapshots definitions in /etc/fstab.

Btrfs Option List

subvol=NAME / subvolid=ID Specifies which subvolume appears under the mount point. subvol=@ mounts the subvolume @ as root. Without this option, the default subvolume is used.

compress=TYPE / compress-force=TYPE Enables transparent compression. Possible types: zlib (default), lzo, zstd. compress-force also compresses files that do not compress well. Important: Compression is incompatible with nodatacow – when nodatacow is active, compress is ignored.

nodatacow Disables copy-on-write for the entire file system. Important: This option affects the whole file system, not just one subvolume. Only the options of the first mounted subvolume are effective. nodatacow implies nodatasum – checksums are no longer calculated.

nodatasum Disables checksums for data. Is automatically enabled with nodatacow.

autodefrag / noautodefrag Enables automatic defragmentation for small, random write operations. Not suitable for databases or VM images. Warning: Can destroy extent sharing with snapshots/reflinks and greatly increase space consumption.

commit=SECONDS Sets the interval for periodic commits. Default: 30 seconds. Higher values delay writing to persistent storage – in the event of a system crash, more data is lost.

ssd / nossd Tells the file system that it is running on an SSD. Btrfs then optimizes block allocation. Often detected automatically on modern kernels.

discard / nodiscard / discard=async discard enables synchronous TRIM on every deletion – can impair performance. discard=async (since kernel 5.6) collects freed blocks and performs TRIM asynchronously – the preferred method. Alternatively: nodiscard and periodic fstrim.

space_cache / space_cache=v1 / space_cache=v2 / nospace_cache Controls the free-space cache. v2 (default since kernel 4.5) is significantly more performant for large file systems. nospace_cache disables the cache.

acl / noacl Enables/disables POSIX ACLs. Default: acl.

barrier / nobarrier barrier (default) ensures that I/O operations go through the device cache and are persistently stored. nobarrier can increase performance but will definitely lead to data loss in the event of a power failure.

degraded Allows mounting with missing devices (e.g., with RAID). A read-write mount can fail if too many devices are missing.

skip_balance Skips the automatic resumption of an interrupted balance operation.

Options for Special Use Cases

Swapfile on Btrfs A swapfile must meet the following conditions:

  • Must be preallocated, no holes
  • Must be NODATACOW (implies NODATASUM) - more on this in the next chapter
  • No compression
  • The containing subvolume must not be snapshotted while the swapfile is active
  • Only a single device and a single data profile

Example for a swap subvolume in fstab:

UUID=... /swap btrfs subvol=@swap,nodatacow,noatime 0 0

/etc/fstab Practical Example

Here is an example fstab with the most important options.

##                                                                                    
UUID=xxxxxxxx-xxxx-xxxx-xxxx   /               btrfs   subvol=@,compress=zstd:3,noatime,ssd,discard=async,space_cache=v2   0       1
UUID=xxxxxxxx-xxxx-xxxx-xxxx   /home           btrfs   subvol=@home,compress=zstd:3,noatime,ssd,discard=async,space_cache=v2  0       2
UUID=xxxxxxxx-xxxx-xxxx-xxxx   /.snapshots     btrfs   subvol=@snapshots,noatime,ssd,space_cache=v2                        0       0

Critical Limitations Regarding nodatacow and compress

  1. Options apply to the entire file system: nodatacow, nodatasum, and compress cannot be controlled per subvolume via mount options. Only the options of the first mounted subvolume count.

  2. chattr +C - To disable copy-on-write only for specific data without affecting the entire file system, setting the C attribute at the file or directory level is the right approach

  3. nodatacow + compress are mutually exclusive: If nodatacow is set, compression is automatically disabled.

  4. nodatacow and snapshots: Snapshots still work, but on the first write after a snapshot, CoW is forced in order to preserve the old blocks. The advantage of nodatacow is thus limited.

  5. nodatacow removes data integrity: Without nodatasum there are no checksums. On multi-disk systems (RAID1), after a crash it cannot be determined which copy is correct – with about a 50% probability the corrupted copy is read.

  6. Avoid autodefrag with snapshots: Auto-defragmentation breaks reflink and snapshot connections and can significantly increase space consumption.

chattr +C instead of nodatacow

To disable copy-on-write only for specific data (e.g., VM images or databases) without affecting the entire file system, setting the C attribute at the file or directory level is the right approach

## Create a new, empty directory (or rename an existing one)
sudo mkdir /var/lib/libvirt/images_nocow
sudo chattr +C /var/lib/libvirt/images_nocow

All newly created files in this directory inherit the NOCOW attribute

For already existing files, a workaround is necessary:

# Rename the directory, create a new one, set the attribute, copy the data back
sudo mv /var/lib/libvirt/images /var/lib/libvirt/images_old
sudo mkdir /var/lib/libvirt/images
sudo chattr +C /var/lib/libvirt/images
sudo cp -a --reflink=never /var/lib/libvirt/images_old/. /var/lib/libvirt/images/
sudo rm -rf /var/lib/libvirt/images_old

The parameter --reflink=never is crucial, because otherwise cp creates a CoW copy (reflink) by default, which does not inherit the attribute.

Important Limitation: Snapshots

Even with chattr +C there is a limitation: When a snapshot is created, it partially breaks the NOCOW property. On the first write to a file after a snapshot, CoW is forced in order to preserve the old blocks for the snapshot. After that, NOCOW applies again until the next snapshot is created.

For VM images or databases that should be excluded from snapshots, it is therefore advisable to place them on a separate partition with nodatacow or possibly another file system.

The Standard Layout in Manjaro

During automatic partitioning, Manjaro creates a large Btrfs partition with several subvolumes. The naming convention traditionally begins with an @ character.

Although the exact names may vary depending on the installer version, the typical layout corresponds to what is described as a convention in the Manjaro wiki:

  • @: The root file system (/).
  • @home: The directory for user data (/home).
  • @snapshots (optional): In the wiki, this subvolume is mentioned as an example of a possible snapshot storage location, but it is not part of the standard installation.

This means: In a standard Manjaro installation, all snapshots that you create, for example with Timeshift or Snapper, are by default located within the @ subvolume under /.snapshots. A separate subvolume for snapshots is not created.

The Standard Layout in TUXEDO OS (Debian-based)

TUXEDO OS uses Btrfs and Snapper by default to create automatic snapshots before and after package updates.

TUXEDO states that during installation six subvolumes are created, which act as separate namespaces within a Btrfs partition. However, the exact names of these six subvolumes are not explicitly listed in the official announcement.

What is known from the official TUXEDO documents:

  • Snapper is preconfigured and automatically creates snapshots.
  • A graphical tool called Btrfs Assistant is installed to manage subvolumes and snapshots.
  • The first snapshot is created during the first boot process and is referred to as the “Factory Image”.

Automation Without External Tools

For regular snapshots, an entry in the crontab is sufficient. A simple shell script creates a timestamped snapshot:

#!/bin/bash
NOW=$(date +"%Y-%m-%d_%H:%M:%S")
mkdir -p /snapshots/home
sudo btrfs subvolume snapshot -r /home "/snapshots/home/home_snapshot_${NOW}"

This script can be executed at fixed intervals via cron or systemd-timer.

  • Snapshots are not backups. They reside on the same file system. In the event of a hardware failure, both the original and the snapshot are lost. For real backups, snapshots must be transferred to another medium, for example with btrfs send.
  • Delete old snapshots regularly. Snapshots take up storage space, even if they initially require hardly any additional space. Over time, differences accumulate. Regular cleanup is necessary.
  • Pay attention to nested subvolumes. If a subvolume contains another subvolume, this is not backed up with the snapshot. The directory entry remains empty. For complete backups, nested subvolumes must be handled separately.

Tool: Snapper - Snapshot Management

Snapper is a tool for managing file system snapshots under Linux. It was originally developed by SUSE and is now available in many distributions.

Snapper automates the creation, management, and cleanup of BTRFS snapshots. It creates snapshots manually, or also automatically during package updates, or also scheduled.

List

With snapper it is more convenient. List snapshots with

snapper list

A typical output of snapper list looks like this:

 # | Type  | Pre # | Date                        | User     | Cleanup     | Description
---+-------+-------+------------------------------+----------+-------------+---------------------------
 0 | single|       |                              | root     |             | current
 1 | single|       | 2026-09-20 08:15:03 +0200    | root     | timeline    | timeline
 2 | pre   |       | 2026-09-22 19:40:11 +0200    | root     | number      | zypp(packagekitd)
 3 | post  |     2 | 2026-09-22 19:41:57 +0200    | root     | number      | zypp(packagekitd)
 4 | single|       | 2026-09-23 03:00:01 +0200    | root     | timeline    | timeline
 5 | pre   |       | 2026-09-25 14:02:33 +0200    | root     | number      | zypp(zypper)
 6 | post  |     5 | 2026-09-25 14:03:12 +0200    | root     | number      | zypp(zypper)

The columns in detail:

  • #: The number of the snapshot. This is used for snapper delete, snapper diff, or snapper undochange.
  • Type: single for standalone snapshots, pre and post for pairs before and after a change.
  • Pre #: For a post snapshot, the number of the associated pre snapshot. For single and pre, the column remains empty.
  • Date: Time of creation.
  • User: The user who triggered the snapshot (usually root).
  • Cleanup: The cleanup strategy. timeline for scheduled snapshots, number for a fixed number, empty-pre-post for orphaned pairs.
  • Description: Free text, for package operations e.g., zypp(zypper) or zypp(packagekitd).

This example also shows why pre/post pairs should be deleted together: Snapshots 5 and 6 belong together, as do 2 and 3. A snapper delete 5-6 removes the pair 5 and 6, a snapper delete 2-3 accordingly removes the pair 2 and 3.

Changes - diff

To see changes between two snapshots, snapper diff is used:

sudo snapper -c root diff 1..3

The output shows which files were added, deleted, or changed between the snapshots.

Deleting

When using snapper, deletion is done via:

sudo snapper -c root delete NUMBER

Here NUMBER is the number of the snapshot from snapper list. Automatically created pre/post pairs – for example before and after a package update – should always be deleted together, since they belong together. Snapper can do this with a single command:

sudo snapper -c root delete NUMBER1-NUMBER2

This removes both snapshots of the pair in one step.

Deletion Strategy

There are situations in which only the post snapshot is removed while the pre snapshot is retained. This makes sense, for example, when:

  • The pre snapshot is to serve as a rollback point: Before an update, a pre snapshot is created. After the update, the post snapshot is normally only needed to document the changes. If you want to keep the rollback point but no longer the comparison basis, you delete only the post.
  • Storage space becomes scarce: The pre snapshot marks the state before the change and is therefore the actually valuable one. The post snapshot is often dispensable if the change was successful.
  • Snapper does not take effect with number cleanup: With the number cleanup strategy, Snapper prefers to delete entire pairs. If a single snapshot remains – for example because the pre was manually marked as worthy of protection – the post may have to be removed manually.

In practice, deleting only the post snapshot is more of an exception. It is customary to remove both together, because the pre snapshot without an associated post is considered orphaned during cleanup and is then automatically removed via the empty-pre-post strategy.

Rolling Back

Snapper offers two variants:

  1. Restore individual files: With snapper undochange, changes between two snapshots are undone. The snapshots themselves are located under /.snapshots/NUMBER/snapshot/ and can also be viewed directly.
  2. Roll back the complete system: With snapper rollback, the root subvolume is reset to an earlier snapshot. The prerequisite is that the desired snapshot has been booted into beforehand.

Restoring Individual Files

A snapshot behaves like a normal directory. When using Snapper, the snapshots are located under /.snapshots/NUMBER/snapshot/:

##### View snapshot contents
ls /.snapshots/5/snapshot/home/hoergen/

##### Copy an individual file back from the snapshot
sudo cp /.snapshots/5/snapshot/home/hoergen/.bashrc /home/hoergen/.bashrc

Alternatively, snapper undochange can be used to undo changes between two snapshots:

##### Undo changes between snapshot 5 (pre) and 6 (post)
sudo snapper -c root undochange 5..6

If only certain files are to be reset, they can be specified explicitly:

sudo snapper -c root undochange 5..6 /etc/fstab /etc/default/grub

Restoring the Complete System - Rollback

For completely rolling back the root file system, Snapper offers the command snapper rollback. The prerequisite is that the system has previously been booted into the desired snapshot. This is done via the boot menu that Snapper provides together with grub-btrfs. In the GRUB menu, an entry “BTRFS Snapshots” appears, from which any snapshot can be booted as the root subvolume.

After booting into the snapshot, the file system is mounted read-only. The rollback is then executed with the following command:

sudo snapper rollback

Optionally with a description:

sudo snapper rollback -d "Rollback after faulty update"

Snapper automatically creates a snapshot of the state before the rollback. After a restart, the system is at the state of the selected snapshot.

Important: snapper rollback only works for the root subvolume /. Other subvolumes such as /home are not rolled back.

Automation with Snapper

Manually creating snapshots is impractical in the long run. Snapper can automatically create timeline snapshots (e.g., hourly, daily, weekly) and delete old ones according to defined rules so that storage space does not run out.

Typical configuration under /etc/snapper/configs/root:

TIMELINE_CREATE="yes"
TIMELINE_CLEANUP="yes"
TIMELINE_LIMIT_HOURLY="6"
TIMELINE_LIMIT_DAILY="7"
TIMELINE_LIMIT_WEEKLY="4"
TIMELINE_LIMIT_MONTHLY="6"
TIMELINE_LIMIT_YEARLY="2"
FREE_LIMIT="0.2"    # Leave at least 20% free

Activating the timers:

sudo systemctl enable --now snapper-timeline.timer
sudo systemctl enable --now snapper-cleanup.timer

This starts the automatic snapshots and the cleanup. Before a pacman -Syu or apt upgrade, a manual snapshot with a description can additionally be created:

sudo snapper -c root create --description "before the update"

After the update, snapper diff can be used to trace what changed. If necessary, a rollback can be performed.

Tool: grub-btrfs

grub-btrfs is a standalone tool that integrates the snapshots created by Snapper into the GRUB menu. After installation, an entry “BTRFS Snapshots” appears in the GRUB menu, from which any snapshot can be booted as the root subvolume. This is the prerequisite for rollback with snapper rollback.

Defragmentation: Options and Problems

The command btrfs filesystem defragment enables defragmentation of files and directory metadata during operation.

However, its use is associated with considerable limitations, especially when snapshots or reflink copies are used.

A reflink (short for “reference link”) is a special type of file copy that modern file systems such as Btrfs or XFS support. It uses the copy-on-write principle (CoW) to create a copy that initially takes up no additional storage space and is almost instantaneous.

Checking Fragmentation

Before performing defragmentation, it should be checked whether there is any relevant fragmentation at all. Btrfs does not report fragmentation automatically. The most reliable way is to measure the extents per file with filefrag and to observe the actual performance behavior.

There is no way to check an entire volume. It only works via filefrag.

An entire Btrfs volume cannot be directly checked as a whole for its degree of fragmentation. There is no command that outputs a single number or percentage for the fragmentation of the entire file system.

The reason lies in the way Btrfs works. Fragmentation is a state that relates to individual files, not to the volume as a whole. A volume can contain thousands of files, some of which are heavily fragmented and others not at all. There is no meaningful single metric that would summarize this.

There is an indirect approach via storage consumption. If btrfs filesystem df shows a large discrepancy between Size (allocated space) and Used (actually occupied space), this indicates fragmented or orphaned extents. However, this is not a measurement of fragmentation, but a reaction to a suspected cause.

Measuring Fragmentation of Individual Files

filefrag is part of the e2fsprogs package and also works on Btrfs thanks to FIEMAP support. A file with a single extent is not fragmented; the more extents, the greater the fragmentation.

## Check fragmentation of a single file
filefrag /home/hoergen/.bashrc

Example output:

/home/hoergen/.bashrc: 3 extents found

Finding the Most Fragmented Files

For an overview of a directory, the output can be sorted. The number of extents is in the second column:

## Top 10 most fragmented files in the current directory
filefrag * | sort -nr -k 2 | head -10

sort -nr -k 2 thus sorts the lines by extent count, descending, with the highest number at the top.

  • -n: Numeric sorting. Without this option, sort would sort alphabetically, so that e.g., 10 would come before 2. With -n, the numeric value is interpreted.

  • -r: Reverse order. Instead of ascending, sorting is descending, so the largest number comes first.

  • -k 2: The sort key is the second column. By default, sort separates columns by spaces. In the output of filefrag, the second column is the number of extents.

  • head -10 outputs the first ten lines of the input by default. Since the input was previously sorted in descending order by extent count, the ten most fragmented files of the directory are shown here.

For the home directory accordingly:

## Top 10 most fragmented files in /home/hoergen
cd /home/hoergen
filefrag * | sort -nr -k 2 | head -10

For snapshots under /snapshots/home/..., a check is only worthwhile if there are actually files there that have been heavily modified. Since snapshots are generally read-only, their fragmentation no longer changes after creation.

Checking Typical Candidates

Certain directories tend to fragment more due to their usage pattern, and it may be worth considering the nodatacow mount option

## Check log directory
filefrag /var/log/journal/* | sort -nr -k 2 | head -10

## Check temporary directory
filefrag /tmp/* 2>/dev/null | sort -nr -k 2 | head -10

Performance as the Actual Indicator

The mere extent count says nothing about actual performance. Defragmentation only makes sense when a noticeable slowdown occurs. Examples:

  • A database under /home/hoergen/db/ responds noticeably more slowly to queries.
  • A log file under /var/log/ slows down system startup.

Without such perceptible effects, on systems with snapshots the risks of defragmentation (loss of extent sharing, increased storage consumption) outweigh the benefits.

Limitation with Snapshots and Reflinks

Before a file is defragmented, it should be checked whether it is connected to a snapshot or a reflink copy. If that is the case, defragmentation breaks this connection and storage consumption increases. A targeted check of whether a file is shared with a snapshot is not directly possible with built-in tools. In practice, this means: Files that are contained in the usual snapshot paths such as /snapshots/home/... or /.snapshots/NUMBER/snapshot/ should not be defragmented.

Options for Defragmentation

Defragmentation can improve I/O performance by rewriting fragmented files into contiguous blocks. The command supports various options:

## Defragment a single file
sudo btrfs filesystem defragment /path/to/file

## Defragment a directory recursively
sudo btrfs filesystem defragment -r /path/to/directory

## With compression (zstd, zlib, or lzo)
sudo btrfs filesystem defragment -r -czstd /path

Important: Without the -r option, only metadata is defragmented, not the file contents. For complete file system defragmentation, -r is required.

Automatic Defragmentation (autodefrag)

The mount option autodefrag enables online defragmentation that automatically intervenes during small, random write operations. This option is suitable for desktop systems with normal usage, but is not recommended for large databases or VM images.

Activation in /etc/fstab:

UUID=... / btrfs defaults,autodefrag,compress=zstd 0 0

The Central Problem: Loss of Extent Sharing

The most critical disadvantage affects systems with snapshots or reflink copies. The official Btrfs documentation states clearly:

Defragmentation does not preserve extent sharing, e.g. files created by cp --reflink or existing on multiple snapshots. Due to that the data space consumption may increase.

What this concretely means:

When a file is defragmented that is currently shared by a snapshot or a reflink clone, the command breaks this sharing. It creates a new, private copy of the data for the defragmented file, while the snapshot continues to point to the old data blocks. Since both copies now exist separately, the storage space requirement for this data doubles.

An example: On a system with Snapper that creates hourly snapshots, defragmenting / can cause storage consumption to rise drastically because every defragmented file loses its connection to all existing snapshots.

Defrag - Conclusion

Aspect Assessment
Performance gain Possible with fragmented files without snapshots
Storage space Increases with snapshots/reflinks due to loss of extent sharing
Snapshot compatibility Not given – snapshots lose their COW connection
Recommendation Only for files without snapshot reference; avoid on Snapper systems

For systems with Snapper or Timeshift, the following applies: Defragmentation should be avoided, unless it is applied specifically to files that are demonstrably not contained in snapshots. The storage space savings from snapshots outweigh the performance loss from fragmentation in most cases.

Defragmenting with Benefits & Risks - Backup

In order to be able to defragment after all, if it should really be necessary, a not really fully thought-out idea would be

  1. create a backup
  2. then delete all snapshots
  3. defragment
  4. create new snapshots
  5. create another backup

Conclusion & Best Practice

BTRFS with snapshots is a practical way to safeguard system changes. The manual commands are manageable, and with Snapper the process can be automated. For systems on which updates or experiments are frequently performed, this is a sensible setup. Switching from ext4 is not trivial, but is worth considering for a fresh installation or a second system.

Best Practice

  1. Standard layout with @ and @home (or as specified by the installer)
  2. Use Snapper for automatic snapshots
  3. Manually create a snapshot before critical actions
  4. Install grub-btrfs for snapper rollback, because it integrates the snapshots into the GRUB menu.
  5. Set fstab options sparingly
  6. Speed: Current performance tests show that BTRFS cannot yet keep up with XFS, ext4, and others, aka is sometimes extremely slower. If you need performance, you should currently use a different FS.

Caution

  1. Setting nodatacow globally
  2. autodefrag in the fstab
  3. Defragmentation on systems with snapshots

Sources

Official Btrfs Documentation

Snapper Documentation

Distributions and Practical Guides

Was ist BTRFS

Weil ich mich gerade intensiv mit BTRFS beschäftige und dazu einige Recherchen durchführe, um für mich Licht in dieses Thema zu bringen, hier eine Auflistung meiner Funde, Erklärungen und Anleitungen. Wenn du Anmerkungen dazu hast, bin ich im Fediverse (About Seite) zu finden. Ansonsten hoffe ich wie immer, dass dieser Artikel dem einen oder der anderen hilft ebenfalls mehr Klarheit in das Thema zu bringen. Und … schreibt selbst mehr Blogartikel!

BTRFS steht für B-Tree File System und ist ein modernes Copy-on-Write-Dateisystem, das ursprünglich 2007 von Chris Mason bei Oracle entwickelt und 2009 in den Linux Kernel 2.6.29 aufgenommen wurde. Es wird heute von verschiedenen Distributionen und Unternehmen unterstützt, darunter Debian, SUSE, Red Hat, Fujitsu und Intel.

Der wesentliche Unterschied zu klassischen Dateisystemen wie ext4 oder NTFS besteht darin, dass BTRFS Daten nicht überschreibt. Wird eine Datei geändert, schreibt BTRFS die neue Version an eine andere Stelle, während die alte Version erhalten bleibt, bis sie nicht mehr benötigt wird.

Daraus ergeben sich mehrere Funktionen:

  • Snapshots: Momentaufnahmen des Dateisystems zu einem bestimmten Zeitpunkt, die anfangs kaum zusätzlichen Speicherplatz belegen.
  • Datenintegrität: BTRFS berechnet Prüfsummen für Daten und Metadaten. Fehler durch defekten RAM oder alternde Datenträger werden erkannt und können bei vorhandener Redundanz (z.B. RAID1) korrigiert werden.
  • Subvolumes: Das Dateisystem lässt sich in logische Teile gliedern, ähnlich Partitionen, aber flexibler.
  • Transparente Komprimierung: Daten können on-the-fly komprimiert werden (z.B. mit zstd).
  • Integrierte RAID-Unterstützung für RAID 0, 1, 10, 5 und 6.

Was sind Snapshots

Ein Snapshot ist eine Momentaufnahme des Dateisystems. Der aktuelle Zustand eines Ordners wird festgehalten und kann später wiederhergestellt werden.

Durch Copy-on-Write belegt ein frischer Snapshot zunächst kaum zusätzlichen Speicherplatz. Er verweist auf dieselben Datenblöcke wie das Original. Erst wenn Änderungen vorgenommen werden, entstehen neue Blöcke, während die alte Version im Snapshot erhalten bleibt. Der zusätzliche Platzverbrauch entspricht also der Differenz zwischen Original und Snapshot.

Daraus ergeben sich typische Einsatzbereiche:

  • Vor System-Updates: Snapshot anlegen, Update durchführen, bei Problemen zurückrollen.
  • Automatische Backups: Tools wie Snapper können in festen Intervallen Snapshots erstellen und alte automatisch löschen.
  • Experimente: Änderungen lassen sich testen und bei Bedarf vollständig rückgängig machen.

Wichtig: Ein Snapshot ist kein Backup. Bei einem Hardware-Ausfall sind Original und Snapshot verloren. Snapshots schützen vor logischen Fehlern wie fehlerhaften Updates oder versehentlichem Löschen, nicht vor Hardware-Ausfällen. Für echte Backups werden Snapshots mit btrfs send und btrfs receive auf ein anderes Medium übertragen.

Hinweise

Snapshots sind kein Ersatz für Backups. Einige Punkte sind zu beachten:

  • Platzverbrauch: Alte Snapshots können über die Zeit viel Platz belegen, da sie die Differenzen zu späteren Änderungen halten. Automatische Bereinigung ist daher sinnvoll.
  • Performance: Bei sehr großen Datenmengen oder Datenbanken kann Copy-on-Write bremsen. Für solche Fälle können Dateien oder Subvolumes mit dem nodatacow-Attribut markiert werden.
  • Kernel und Bootloader: Wird /boot in den Snapshot einbezogen, kann ein Rollback problematisch werden, wenn Kernel und Module nicht mehr zusammenpassen. Viele Distributionen schließen /boot daher aus oder behandeln den Kernel separat.

Snapshots mit btrfs “tool”

Snapshots lassen sich vollständig ohne zusätzliche Werkzeuge wie Snapper oder Timeshift erstellen und verwalten. Die notwendigen Befehle sind Teil von btrfs-progs, das auf jedem System mit Btrfs-Unterstützung vorhanden ist.

Snapshot erstellen

Ein Snapshot wird mit btrfs subvolume snapshot erstellt. Der Befehl benötigt ein Quell-Subvolume und ein Zielverzeichnis für den Snapshot.

Schreibgeschützter Snapshot (empfohlen für Backups):

sudo btrfs subvolume snapshot -r /home /snapshots/home/home_snapshot_$(date +%Y%m%d_%H%M%S)

Die Option -r erzeugt einen schreibgeschützten Snapshot. Dieser kann nicht versehentlich verändert werden und eignet sich für die spätere Übertragung mit btrfs send.

Schreibbarer Snapshot (für Tests oder Klone):

sudo btrfs subvolume snapshot /home /snapshots/home/home_snapshot_ro

Ohne -r entsteht ein beschreibbarer Snapshot, der wie eine vollständige Kopie des Dateisystems genutzt werden kann.

Wichtig: Snapshots können nur von Subvolumes erstellt werden, nicht von normalen Verzeichnissen. Wenn die Quelle kein Subvolume ist, gibt Btrfs einen Fehler zurück. Das Zielverzeichnis /snapshots/home/ muss dabei auf einem eigenen Subvolume liegen oder außerhalb des Quell-Subvolumes, damit keine rekursive Struktur entsteht.

Snapshot auflisten

Alle Subvolumes und Snapshots lassen sich mit folgendem Befehl anzeigen:

sudo btrfs subvolume list /

Die Ausgabe zeigt unter anderem die ID, die übergeordnete ID und den Pfad jedes Subvolumes. Mit der Option -s werden nur Snapshots aufgelistet, mit -r nur schreibgeschützte.

Eine typische Ausgabe von sudo btrfs subvolume list / sieht so aus:

ID 256 gen 12345 top level 5 path @
ID 257 gen 12340 top level 5 path @home
ID 258 gen 12200 top level 5 path @snapshots
ID 259 gen 12050 top level 5 path snapshots/home/home_snapshot_2026-03-14
ID 260 gen 11980 top level 5 path snapshots/home/home_snapshot_ro

Die Spalten im Einzelnen:

  • ID: Die eindeutige Nummer des Subvolumes. Diese wird intern von Btrfs verwendet und ist nicht mit der Snapshot-Nummer von Snapper zu verwechseln.
  • gen: Die Generation, also ein Zähler, der bei jeder Transaktion im Dateisystem erhöht wird. Ein höherer Wert bedeutet, dass das Subvolume später zuletzt geändert wurde.
  • top level: Die ID des übergeordneten Subvolumes. Der Wert 5 steht für das Top-Level-Subvolume, also die Wurzel des Btrfs-Dateisystems.
  • path: Der Pfad des Subvolumes relativ zum Top-Level-Subvolume. Hier wird sichtbar, wo das Subvolume im Baum hängt.

In diesem Beispiel erkennt man:

  • @ ist das Root-Subvolume (ID 256).
  • @home ist ein separates Subvolume für /home (ID 257).
  • @snapshots ist ein eigenes Subvolume (ID 258).
  • snapshots/home/home_snapshot_2026-03-14 und snapshots/home/home_snapshot_ro sind die Snapshots, die unter dem Pfad /snapshots/home/ angelegt wurden.

Hinweis: Die Ausgabe zeigt die Pfade relativ zum Top-Level-Subvolume, nicht relativ zum gemounteten Root. Wenn /snapshots als separates Subvolume eingebunden ist, erscheint der Snapshot-Pfad entsprechend unter snapshots/home/.... Wird /snapshots nicht separat gemountet, liegen die Snapshots innerhalb des Root-Subvolumes und tauchen als @/snapshots/home/... auf.

Mit der Option -s werden nur Snapshots angezeigt:

sudo btrfs subvolume list -s /

Mit der Option -r nur schreibgeschützte Subvolumes:

sudo btrfs subvolume list -r /

Mit der Option -t wird zusätzlich die Art des Subvolumes ausgegeben (snapshot oder subvolume):

sudo btrfs subvolume list -t /

Snapshot löschen

Ein Snapshot wird nicht mit rm -rf entfernt, sondern mit:

sudo btrfs subvolume delete /snapshots/home/home_snapshot_ro

Das entsprechende Verzeichnis wird sofort entfernt, die eigentlichen Datenblöcke werden jedoch im Hintergrund gelöscht. Der Befehl kehrt sofort zurück, ohne auf den Abschluss der Datenlöschung zu warten.

Soll gewartet werden, bis die Löschung vollständig abgeschlossen ist:

sudo btrfs subvolume sync /snapshots/home

Snapshot zurückrollen

Für das Zurückrollen auf einen Snapshot wird das aktuelle Subvolume verschoben und der Snapshot an seine Stelle kopiert:

## Aktuelles Subvolume als Backup umbenennen
sudo mv /mnt/btrfs_root/@home /mnt/btrfs_root/@home_backup_$(date +%Y%m%d)

## Snapshot als neues Subvolume kopieren
sudo btrfs subvolume snapshot \
  /snapshots/home/home_snapshot_20260301 \
  /mnt/btrfs_root/@home

Nach einem erneuten Einbinden steht der wiederhergestellte Zustand zur Verfügung. Der alte Zustand bleibt unter @home_backup_... erhalten, bis er manuell gelöscht wird.

Snapshots sichern mit send/receive

Für echte Backups außerhalb des Systems dienen btrfs send und btrfs receive. Beide Operationen erfordern schreibgeschützte Snapshots.

Initiales Backup:

sudo btrfs send /snapshots/home/home_snapshot_ro | btrfs receive /backupvol

Inkrementelles Backup (nur Änderungen seit dem letzten Snapshot):

sudo btrfs send -p /snapshots/home/home_snapshot_ro /snapshots/home/home_snapshot_20260314 | btrfs receive /backupvol

Die Option -p gibt den vorherigen Snapshot als Parent an. Übertragen werden nur die Differenzen zwischen beiden Snapshots.

@snapshots anlegen, konfigurieren und verwenden

Der Speicherort für Snapshots wird im Btrfs-Subvolume-Layout und in /etc/fstab festgelegt, nicht in Snappers Konfigurationsdatei. In Snappers Konfiguration gibt SUBVOLUME lediglich an, für welches Verzeichnis Snapshots erstellt werden sollen (z. B. /). Ob die Snapshots im selben Subvolume verbleiben oder ausgelagert werden, entscheidet die Konfiguration über die Btrfs-Subvolume-Struktur und die Mount-Einträge in /etc/fstab.

In der Snapper Konfiguration wird Bezug auf die @snapshots Definitionen in der /etc/fstab genommen.

Btrfs-Optionsliste

subvol=NAME / subvolid=ID Legt fest, welches Subvolume unter dem Einhängepunkt erscheint. subvol=@ mountet das Subvolume @ als Root. Ohne diese Option wird das Standard-Subvolume verwendet .

compress=TYPE / compress-force=TYPE Aktiviert transparente Kompression. Mögliche Typen: zlib (Standard), lzo, zstd. compress-force komprimiert auch schlecht komprimierbare Dateien. Wichtig: Komprimierung ist mit nodatacow unvereinbar – bei aktivem nodatacow wird compress ignoriert .

nodatacow Deaktiviert Copy-on-Write für das gesamte Dateisystem. Wichtig: Diese Option wirkt sich auf das ganze Dateisystem aus, nicht nur auf ein Subvolume. Nur die Optionen des zuerst gemounteten Subvolumes sind wirksam . nodatacow impliziert nodatasum – es werden keine Prüfsummen mehr berechnet .

nodatasum Deaktiviert Prüfsummen für Daten. Wird automatisch bei nodatacow aktiviert .

autodefrag / noautodefrag Aktiviert automatische Defragmentierung bei kleinen, zufälligen Schreibvorgängen. Nicht geeignet für Datenbanken oder VM-Images . Warnung: Kann bei Snapshots/Reflinks die Extent-Freigabe zerstören und den Speicherverbrauch stark erhöhen .

commit=SECONDS Setzt das Intervall für periodische Commits. Standard: 30 Sekunden. Höhere Werte verzögern das Schreiben auf persistente Speicherung – bei Systemabsturz gehen mehr Daten verloren .

ssd / nossd Teilt dem Dateisystem mit, dass es auf einem SSD läuft. Btrfs optimiert dann die Block-Allokation. Auf modernen Kerneln oft automatisch erkannt .

discard / nodiscard / discard=async discard aktiviert synchrones TRIM bei jedem Löschen – kann die Performance beeinträchtigen. discard=async (seit Kernel 5.6) sammelt freigegebene Blöcke und führt TRIM asynchron aus – die bevorzugte Methode. Alternativ: nodiscard und periodisches fstrim .

space_cache / space_cache=v1 / space_cache=v2 / nospace_cache Steuert den Free-Space-Cache. v2 (Standard seit Kernel 4.5) ist für große Dateisysteme deutlich performanter. nospace_cache deaktiviert den Cache .

acl / noacl Aktiviert/deaktiviert POSIX ACLs. Standard: acl .

barrier / nobarrier barrier (Standard) stellt sicher, dass I/O-Operationen durch den Geräte-Cache gehen und persistent gespeichert werden. nobarrier kann die Performance erhöhen, führt bei Stromausfall aber mit Sicherheit zu Datenverlust .

degraded Erlaubt das Mounten mit fehlenden Geräten (z. B. bei RAID). Ein Read-Write-Mount kann fehlschlagen, wenn zu viele Geräte fehlen .

skip_balance Überspringt die automatische Wiederaufnahme einer unterbrochenen Balance-Operation .

Optionen für spezielle Einsatzzwecke

Swapfile auf Btrfs Ein Swapfile muss folgende Bedingungen erfüllen :

  • Muss vorgelagert (preallocated) sein, keine Holes
  • Muss NODATACOW sein (impliziert NODATASUM) - mehr im nächsten Kapitel
  • Keine Kompression
  • Das enthaltende Subvolume darf nicht snapshottet werden, solange das Swapfile aktiv ist
  • Nur ein einziges Gerät und ein einziges Datenprofil

Beispiel für ein Swap-Subvolume in der fstab:

UUID=... /swap btrfs subvol=@swap,nodatacow,noatime 0 0

/etc/stab Praktisches Beispiel

Hier eine Beispiel fstab mit den wichtigsten Optionen.

##                                                                                    
UUID=xxxxxxxx-xxxx-xxxx-xxxx   /               btrfs   subvol=@,compress=zstd:3,noatime,ssd,discard=async,space_cache=v2   0       1
UUID=xxxxxxxx-xxxx-xxxx-xxxx   /home           btrfs   subvol=@home,compress=zstd:3,noatime,ssd,discard=async,space_cache=v2  0       2
UUID=xxxxxxxx-xxxx-xxxx-xxxx   /.snapshots     btrfs   subvol=@snapshots,noatime,ssd,space_cache=v2                        0       0

Kritische Einschränkungen zu nodatacow und compress

  1. Optionen gelten für das gesamte Dateisystem: nodatacow, nodatasum und compress können nicht pro Subvolume über Mount-Optionen gesteuert werden. Nur die Optionen des ersten gemounteten Subvolumes zählen .

  2. chattr +C - Um Copy-on-Write nur für bestimmte Daten zu deaktivieren, ohne das gesamte Dateisystem zu beeinträchtigen, ist das Setzen des C-Attributs auf Datei- oder Verzeichnisebene der richtige Weg

  3. nodatacow + compress schließen sich aus: Wird nodatacow gesetzt, ist Kompression automatisch deaktiviert .

  4. nodatacow und Snapshots: Snapshots funktionieren weiterhin, aber beim ersten Schreiben nach einem Snapshot wird CoW erzwungen, um die alten Blöcke zu bewahren. Der Vorteil von nodatacow ist damit eingeschränkt .

  5. nodatacow entfernt Datenintegrität: Ohne nodatasum gibt es keine Prüfsummen. Auf Multi-Disk-Systemen (RAID1) kann nach einem Absturz nicht festgestellt werden, welche Kopie korrekt ist – mit etwa 50% Wahrscheinlichkeit wird die beschädigte Kopie gelesen .

  6. autodefrag bei Snapshots vermeiden: Auto-Defragmentierung bricht Reflink- und Snapshot-Verbindungen auf und kann den Speicherverbrauch erheblich steigern .

chattr +C statt nodatacow

Um Copy-on-Write nur für bestimmte Daten (z. B. VM-Images oder Datenbanken) zu deaktivieren, ohne das gesamte Dateisystem zu beeinträchtigen, ist das Setzen des C-Attributs auf Datei- oder Verzeichnisebene der richtige Weg

## Neues, leeres Verzeichnis erstellen (oder ein bestehendes umbenennen)
sudo mkdir /var/lib/libvirt/images_nocow
sudo chattr +C /var/lib/libvirt/images_nocow

Alle neu erstellten Dateien in diesem Verzeichnis erben das NOCOW-Attribut

Für bereits existierende Dateien ist ein Workaround nötig:

# Verzeichnis umbenennen, neu erstellen, Attribut setzen, Daten zurückkopieren
sudo mv /var/lib/libvirt/images /var/lib/libvirt/images_old
sudo mkdir /var/lib/libvirt/images
sudo chattr +C /var/lib/libvirt/images
sudo cp -a --reflink=never /var/lib/libvirt/images_old/. /var/lib/libvirt/images/
sudo rm -rf /var/lib/libvirt/images_old

Der Parameter --reflink=never ist entscheidend, da cp sonst standardmäßig eine CoW-Kopie (Reflink) erstellt, die das Attribut nicht übernimmt.

Wichtige Einschränkung: Snapshots

Auch mit chattr +C gibt es eine Einschränkung: Wenn ein Snapshot erstellt wird, bricht dieser die NOCOW-Eigenschaft teilweise auf. Beim ersten Schreiben in eine Datei nach einem Snapshot wird CoW erzwungen, um die alten Blöcke für den Snapshot zu bewahren. Danach greift wieder NOCOW, bis der nächste Snapshot erstellt wird.

Für VM-Images oder Datenbanken, die von Snapshots ausgeschlossen werden sollen, empfiehlt es sich daher, sie in eine eigene Partition mit mit nodatacow oder eventuell anderem Dateisystem anzulegen.

Das Standard-Layout bei Manjaro

Manjaro legt bei der automatischen Partitionierung eine große Btrfs-Partition mit mehreren Subvolumes an. Die Namenskonvention beginnt traditionell mit einem @-Zeichen.

Obwohl die genauen Namen je nach Installer-Version variieren können, entspricht das typische Layout dem, was auch in der Manjaro-Wiki als Konvention beschrieben wird:

  • @: Das Root-Dateisystem (/).
  • @home: Das Verzeichnis für Benutzerdaten (/home).
  • @snapshots (optional): In der Wiki wird dieses Subvolume als Beispiel für einen möglichen Snapshot-Speicherort erwähnt, aber es ist nicht Teil der Standard-Installation.

Das bedeutet: Bei einer Standard-Manjaro-Installation liegen alle Snapshots, die du beispielsweise mit Timeshift oder Snapper erstellst, standardmäßig innerhalb des @-Subvolumes unter /.snapshots. Ein separates Subvolume für Snapshots wird nicht erstellt.

Das Standard-Layout bei TUXEDO OS (Debian-Basis)

TUXEDO OS nutzt Btrfs und Snapper standardmäßig, um automatische Snapshots vor und nach Paket-Updates zu erstellen.

TUXEDO gibt an, dass bei der Installation sechs Subvolumes angelegt werden, die als getrennte Namensräume innerhalb einer Btrfs-Partition fungieren. Die genauen Namen dieser sechs Subvolumes werden in der offiziellen Ankündigung jedoch nicht explizit aufgeführt.

Was aus den offiziellen TUXEDO-Dokumenten bekannt ist:

  • Snapper ist vorkonfiguriert und erstellt automatisch Snapshots.
  • Ein grafisches Tool namens Btrfs Assistant ist installiert, um Subvolumes und Snapshots zu verwalten.
  • Der erste Snapshot wird bereits beim ersten Bootvorgang erstellt und als „Factory Image“ bezeichnet.

Automatisierung ohne externe Tools

Für regelmäßige Snapshots genügt ein Eintrag in der Crontab. Ein einfaches Shell-Skript erstellt einen zeitgestempelten Snapshot:

#!/bin/bash
NOW=$(date +"%Y-%m-%d_%H:%M:%S")
mkdir -p /snapshots/home
sudo btrfs subvolume snapshot -r /home "/snapshots/home/home_snapshot_${NOW}"

Dieses Skript kann per cron oder systemd-timer in festen Intervallen ausgeführt werden.

  • Snapshots sind keine Backups. Sie liegen auf demselben Dateisystem. Bei einem Hardware-Ausfall sind Original und Snapshot verloren. Für echte Backups müssen Snapshots auf ein anderes Medium übertragen werden, etwa mit btrfs send.
  • Alte Snapshots regelmäßig löschen. Snapshots belegen Speicherplatz, auch wenn sie anfangs kaum zusätzlichen Platz benötigen. Mit der Zeit sammeln sich Differenzen an. Ein regelmäßiges Aufräumen ist notwendig.
  • Nested Subvolumes beachten. Wenn ein Subvolume ein anderes Subvolume enthält, wird dieses beim Snapshot nicht mitgesichert. Der Verzeichniseintrag bleibt leer. Für vollständige Sicherungen müssen verschachtelte Subvolumes separat behandelt werden.

Tool: Snapper - Snapshotverwaltung

Snapper ist ein Werkzeug zur Verwaltung von Dateisystem-Snapshots unter Linux. Es wurde ursprünglich von SUSE entwickelt und ist heute in vielen Distributionen verfügbar.

Snapper automatisiert das Erstellen, Verwalten und Bereinigen von BTRFS-Snapshots. Es legt Snapshots manuell, oder auch automatisch bei Paket-Updates oder auch zeitgesteuert an.

List

Mit snapper geht es komfortabler. Snapshots auflisten mit

snapper list

Eine typische Ausgabe von snapper list sieht so aus:

 # | Typ   | Vor # | Datum                        | Benutzer | Bereinigung | Beschreibung
---+-------+-------+------------------------------+----------+-------------+---------------------------
 0 | single|       |                              | root     |             | current
 1 | single|       | 2026-09-20 08:15:03 +0200    | root     | timeline    | timeline
 2 | pre   |       | 2026-09-22 19:40:11 +0200    | root     | number      | zypp(packagekitd)
 3 | post  |     2 | 2026-09-22 19:41:57 +0200    | root     | number      | zypp(packagekitd)
 4 | single|       | 2026-09-23 03:00:01 +0200    | root     | timeline    | timeline
 5 | pre   |       | 2026-09-25 14:02:33 +0200    | root     | number      | zypp(zypper)
 6 | post  |     5 | 2026-09-25 14:03:12 +0200    | root     | number      | zypp(zypper)

Die Spalten im Einzelnen:

  • #: Die Nummer des Snapshots. Diese wird für snapper delete, snapper diff oder snapper undochange verwendet.
  • Typ: single für eigenständige Snapshots, pre und post für Paare vor und nach einer Änderung.
  • Vor #: Bei einem post-Snapshot die Nummer des zugehörigen pre-Snapshots. Bei single und pre bleibt die Spalte leer.
  • Datum: Zeitpunkt der Erstellung.
  • Benutzer: Der Benutzer, der den Snapshot ausgelöst hat (meist root).
  • Bereinigung: Die Cleanup-Strategie. timeline für zeitgesteuerte Snapshots, number für eine feste Anzahl, empty-pre-post für verwaiste Paare.
  • Beschreibung: Freitext, bei Paketoperationen etwa zypp(zypper) oder zypp(packagekitd).

An diesem Beispiel sieht man auch, warum Pre/Post-Paare gemeinsam gelöscht werden sollten: Snapshot 5 und 6 gehören zusammen, ebenso 2 und 3. Ein snapper delete 5-6 entfernt das Paar 5 und 6, ein snapper delete 2-3 entsprechend das Paar 2 und 3.

Änderungen - diff

Um Änderungen zwischen zwei Snapshots zu sehen, dient snapper diff:

sudo snapper -c root diff 1..3

Die Ausgabe zeigt, welche Dateien zwischen den Snapshots hinzugefügt, gelöscht oder geändert wurden.

Löschen

Bei Verwendung von snapper erfolgt das Löschen über:

sudo snapper -c root delete NUMBER

Dabei ist NUMBER die Nummer des Snapshots aus snapper list. Automatisch erstellte Pre/Post-Paare – etwa vor und nach einem Paket-Update – sollten immer gemeinsam gelöscht werden, da sie zusammengehören. Snapper kann das mit einem Befehl erledigen:

sudo snapper -c root delete NUMBER1-NUMBER2

Damit werden beide Snapshots des Paares in einem Schritt entfernt.

Lösch Strategie

Es gibt Situationen, in denen nur der post-Snapshot entfernt wird, während der pre-Snapshot erhalten bleibt. Sinnvoll ist das zum Beispiel, wenn:

  • Der Pre-Snapshot als Rollback-Punkt dienen soll: Vor einem Update wird ein pre-Snapshot angelegt. Nach dem Update wird der post-Snapshot normalerweise nur zur Dokumentation der Änderungen benötigt. Will man den Rollback-Punkt behalten, aber die Vergleichsbasis nicht mehr, löscht man nur den post.
  • Speicherplatz knapp wird: Der pre-Snapshot markiert den Zustand vor der Änderung und ist damit der eigentlich wertvolle. Der post-Snapshot ist oft entbehrlich, wenn die Änderung erfolgreich war.
  • Snapper bei number-Cleanup nicht greift: Snapper löscht bei der Cleanup-Strategie number bevorzugt ganze Paare. Bleibt ein einzelner Snapshot übrig – etwa weil der pre manuell als schützenswert markiert wurde – muss der post unter Umständen manuell entfernt werden.

In der Praxis ist das Löschen nur des post-Snapshots eher die Ausnahme. Üblich ist, dass beide gemeinsam entfernt werden, weil der pre-Snapshot ohne zugehörigen post bei der Bereinigung als verwaist gilt und dann über die Strategie empty-pre-post automatisch entfernt wird.

Zurückrollen

Snapper bietet zwei Varianten:

  1. Einzelne Dateien wiederherstellen: Mit snapper undochange werden Änderungen zwischen zwei Snapshots rückgängig gemacht. Die Snapshots selbst liegen unter /.snapshots/NUMBER/snapshot/ und können auch direkt eingesehen werden.
  2. Komplettes System zurückrollen: Mit snapper rollback wird das Root-Subvolume auf einen früheren Snapshot zurückgesetzt. Voraussetzung ist, dass zuvor in den gewünschten Snapshot gebootet wurde.

Wiederherstellen einzelne Dateien

Ein Snapshot verhält sich wie ein normales Verzeichnis. Bei Verwendung von Snapper liegen die Snapshots unter /.snapshots/NUMBER/snapshot/:

##### Snapshot-Inhalt ansehen
ls /.snapshots/5/snapshot/home/hoergen/

##### Einzelne Datei aus dem Snapshot zurückkopieren
sudo cp /.snapshots/5/snapshot/home/hoergen/.bashrc /home/hoergen/.bashrc

Alternativ kann snapper undochange verwendet werden, um Änderungen zwischen zwei Snapshots rückgängig zu machen:

##### Änderungen zwischen Snapshot 5 (pre) und 6 (post) rückgängig machen
sudo snapper -c root undochange 5..6

Sollen nur bestimmte Dateien zurückgesetzt werden, können diese explizit angegeben werden:

sudo snapper -c root undochange 5..6 /etc/fstab /etc/default/grub

Komplettes System wiederherstellen - Rollback

Für das vollständige Zurückrollen des Root-Dateisystems bietet Snapper den Befehl snapper rollback. Voraussetzung ist, dass das System zuvor in den gewünschten Snapshot gebootet wurde. Dies geschieht über das Boot-Menü, das Snapper zusammen mit grub-btrfs bereitstellt. Im GRUB-Menü erscheint ein Eintrag „BTRFS Snapshots", aus dem ein beliebiger Snapshot als Root-Subvolume gebootet werden kann.

Nach dem Booten in den Snapshot ist das Dateisystem schreibgeschützt eingebunden. Der Rollback wird dann mit folgendem Befehl ausgeführt:

sudo snapper rollback

Optional mit Beschreibung:

sudo snapper rollback -d "Rollback nach fehlerhaftem Update"

Snapper erstellt dabei automatisch einen Snapshot des Zustands vor dem Rollback. Nach einem Neustart ist das System auf dem Stand des gewählten Snapshots.

Wichtig: snapper rollback funktioniert nur für das Root-Subvolume /. Andere Subvolumes wie /home werden nicht mit zurückgesetzt.

Automatisierung mit Snapper

Manuelles Erstellen von Snapshots ist auf Dauer unpraktisch. Snapper kann automatisch Timeline-Snapshots anlegen (z.B. stündlich, täglich, wöchentlich) und alte nach definierten Regeln löschen, damit der Speicherplatz nicht vollläuft.

Typische Konfiguration unter /etc/snapper/configs/root:

TIMELINE_CREATE="yes"
TIMELINE_CLEANUP="yes"
TIMELINE_LIMIT_HOURLY="6"
TIMELINE_LIMIT_DAILY="7"
TIMELINE_LIMIT_WEEKLY="4"
TIMELINE_LIMIT_MONTHLY="6"
TIMELINE_LIMIT_YEARLY="2"
FREE_LIMIT="0.2"    # Mindestens 20% frei lassen

Aktivierung der Timer:

sudo systemctl enable --now snapper-timeline.timer
sudo systemctl enable --now snapper-cleanup.timer

Damit laufen die automatischen Snapshots und die Bereinigung. Vor einem pacman -Syu oder apt upgrade kann zusätzlich ein manueller Snapshot mit Beschreibung angelegt werden:

sudo snapper -c root create --description "vor dem Update"

Nach dem Update lässt sich mit snapper diff nachvollziehen, was sich geändert hat. Bei Bedarf kann zurückgerollt werden.

Tool: grub-btrfs

grub-btrfs ist ein eigenständiges Tool, das die von Snapper erstellten Snapshots in das GRUB-Menü einbindet. Nach der Installation erscheint im GRUB-Menü ein Eintrag „BTRFS Snapshots", aus dem heraus ein beliebiger Snapshot als Root-Subvolume gebootet werden kann. Dies ist die Voraussetzung für den Rollback mit snapper rollback.

Defragmentierung: Möglichkeiten und Probleme

Der Befehl btrfs filesystem defragment ermöglicht die Defragmentierung von Dateien und Verzeichnis-Metadaten im laufenden Betrieb.

Die Anwendung ist jedoch mit erheblichen Einschränkungen verbunden, insbesondere beim Einsatz von Snapshots oder Reflink-Kopien.

Ein Reflink (kurz für „Reference Link“) ist eine spezielle Art von Dateikopie, die moderne Dateisysteme wie Btrfs oder XFS unterstützen. Sie nutzt das Copy-on-Write-Prinzip (CoW), um eine Kopie zu erstellen, die zunächst keinen zusätzlichen Speicherplatz belegt und nahezu augenblicklich fertig ist

Fragmentierung prüfen

Bevor eine Defragmentierung durchgeführt wird, sollte geprüft werden, ob überhaupt eine relevante Fragmentierung vorliegt. Btrfs meldet Fragmentierung nicht automatisch. Der zuverlässigste Weg ist die Messung der Extents pro Datei mit filefrag sowie die Beobachtung des tatsächlichen Performance-Verhaltens.

Es gibt keine Möglichkeit ein ganzes Volume zu prüfen. Es geht nur über filefrag

Ein ganzes Btrfs-Volume lässt sich nicht direkt als Ganzes auf seinen Fragmentierungsgrad prüfen. Es gibt keinen Befehl, der eine einzelne Zahl oder einen Prozentsatz für die Fragmentierung des gesamten Dateisystems ausgibt.

Der Grund liegt in der Arbeitsweise von Btrfs. Fragmentierung ist ein Zustand, der sich auf einzelne Dateien bezieht, nicht auf das Volume als Ganzes. Ein Volume kann Tausende von Dateien enthalten, von denen einige stark fragmentiert und andere gar nicht fragmentiert sind. Es gibt keine sinnvolle einzelne Kennzahl, die das zusammenfassen würde.

Es gibt einen indirekten Ansatz über den Speicherverbrauch. Wenn btrfs filesystem df eine große Diskrepanz zwischen Size (allokierter Platz) und Used (tatsächlich belegter Platz) zeigt, deutet das auf fragmentierte oder verwaiste Extents hin. Das ist aber keine Messung der Fragmentierung, sondern eine Reaktion auf eine vermutete Ursache.

Fragmentierung einzelner Dateien messen

filefrag ist Teil des Pakets e2fsprogs und funktioniert dank FIEMAP-Unterstützung auch auf Btrfs. Eine Datei mit einem einzigen Extent ist nicht fragmentiert; je mehr Extents, desto stärker die Fragmentierung.

## Fragmentierung einer einzelnen Datei prüfen
filefrag /home/hoergen/.bashrc

Beispielausgabe:

/home/hoergen/.bashrc: 3 extents found

Die am stärksten fragmentierten Dateien finden

Für einen Überblick über ein Verzeichnis kann die Ausgabe sortiert werden. Die Zahl der Extents steht in der zweiten Spalte:

## Top 10 der fragmentiertesten Dateien im aktuellen Verzeichnis
filefrag * | sort -nr -k 2 | head -10

sort -nr -k 2 die Zeilen also nach der Extent-Anzahl, absteigend, mit der höchsten Zahl oben.

  • -n: Numerische Sortierung. Ohne diese Option würde sort alphabetisch sortieren, sodass z. B. 10 vor 2 käme. Mit -n wird der Zahlenwert interpretiert.

  • -r: Umgekehrte Reihenfolge (reverse). Statt aufsteigend wird absteigend sortiert, also die größte Zahl zuerst.

  • -k 2: Sortierschlüssel ist die zweite Spalte. Standardmäßig trennt sort Spalten an Leerzeichen. In der Ausgabe von filefrag ist die zweite Spalte die Anzahl der Extents.

  • head -10 gibt standardmäßig die ersten zehn Zeilen der Eingabe aus. Da die Eingabe zuvor absteigend nach Extent-Anzahl sortiert wurde, stehen hier die zehn am stärksten fragmentierten Dateien des Verzeichnisses.

Für das Home-Verzeichnis entsprechend:

## Top 10 der fragmentiertesten Dateien in /home/hoergen
cd /home/hoergen
filefrag * | sort -nr -k 2 | head -10

Für Snapshots unter /snapshots/home/... ist eine Prüfung nur dann sinnvoll, wenn dort tatsächlich Dateien liegen, die stark verändert wurden. Da Snapshots in der Regel schreibgeschützt sind, ändert sich ihre Fragmentierung nach der Erstellung nicht mehr.

Typische Kandidaten prüfen

Bestimmte Verzeichnisse neigen aufgrund ihres Nutzungsmusters stärker zur Fragmentierung und eventuell mount Option nodatacow in Betracht ziehen

## Log-Verzeichnis prüfen
filefrag /var/log/journal/* | sort -nr -k 2 | head -10

## Temporäres Verzeichnis prüfen
filefrag /tmp/* 2>/dev/null | sort -nr -k 2 | head -10

Performance als eigentlicher Indikator

Die reine Extent-Zahl sagt noch nichts über die tatsächliche Leistung aus. Eine Defragmentierung ist erst dann sinnvoll, wenn eine spürbare Verlangsamung auftritt. Beispiele:

  • Eine Datenbank unter /home/hoergen/db/ reagiert merklich langsamer bei Abfragen.
  • Eine Logdatei unter /var/log/ verlangsamt den Systemstart.

Ohne solche wahrnehmbaren Effekte überwiegen bei Systemen mit Snapshots die Risiken der Defragmentierung (Verlust der Extent-Freigabe, erhöhter Speicherverbrauch) den Nutzen.

Einschränkung bei Snapshots und Reflinks

Bevor eine Datei defragmentiert wird, sollte geprüft werden, ob sie mit einem Snapshot oder einer Reflink-Kopie verbunden ist. Ist das der Fall, bricht eine Defragmentierung diese Verbindung auf und der Speicherverbrauch steigt. Eine gezielte Prüfung, ob eine Datei mit einem Snapshot geteilt wird, ist mit Bordmitteln nicht direkt möglich. In der Praxis bedeutet das: Dateien, die in den üblichen Snapshot-Pfaden wie /snapshots/home/... oder /.snapshots/NUMBER/snapshot/ enthalten sind, sollten nicht defragmentiert werden.

Möglichkeiten der Defragmentierung

Defragmentierung kann die I/O-Performance verbessern, indem fragmentierte Dateien in zusammenhängende Blöcke umgeschrieben werden. Der Befehl unterstützt verschiedene Optionen:

## Einzelne Datei defragmentieren
sudo btrfs filesystem defragment /pfad/zur/datei

## Verzeichnis rekursiv defragmentieren
sudo btrfs filesystem defragment -r /pfad/zum/verzeichnis

## Mit Kompression (zstd, zlib oder lzo)
sudo btrfs filesystem defragment -r -czstd /pfad

Wichtig: Ohne die Option -r werden nur Metadaten defragmentiert, nicht die Dateiinhalte . Für eine vollständige Dateisystem-Defragmentierung ist -r erforderlich.

Automatische Defragmentierung (autodefrag)

Die Mount-Option autodefrag aktiviert eine Online-Defragmentierung, die bei kleinen, zufälligen Schreibvorgängen automatisch eingreift . Diese Option eignet sich für Desktop-Systeme mit normaler Nutzung, wird aber nicht für große Datenbanken oder VM-Images empfohlen .

Aktivierung in /etc/fstab:

UUID=... / btrfs defaults,autodefrag,compress=zstd 0 0

Das zentrale Problem: Verlust der Extent-Freigabe

Der kritischste Nachteil betrifft Systeme mit Snapshots oder Reflink-Kopien. Die offizielle Btrfs-Dokumentation stellt klar fest:

Defragmentation does not preserve extent sharing, e.g. files created by cp --reflink or existing on multiple snapshots. Due to that the data space consumption may increase.

Was das konkret bedeutet:

Wenn eine Datei defragmentiert wird, die gerade von einem Snapshot oder einem Reflink-Klon mitbenutzt wird, bricht der Befehl diese gemeinsame Nutzung auf. Er erstellt eine neue, private Kopie der Daten für die defragmentierte Datei, während der Snapshot weiterhin auf die alten Datenblöcke verweist. Da beide Kopien nun separat existieren, verdoppelt sich der Speicherplatzbedarf für diese Daten .

Ein Beispiel: Bei einem System mit Snapper, das stündliche Snapshots erstellt, kann eine Defragmentierung von / dazu führen, dass der Speicherverbrauch drastisch ansteigt, weil jede defragmentierte Datei ihre Verbindung zu allen bestehenden Snapshots verliert .

Defrag - Fazit

Aspekt Bewertung
Performance-Gewinn Möglich bei fragmentierten Dateien ohne Snapshots
Speicherplatz Erhöht sich bei Snapshots/Reflinks durch Verlust der Extent-Freigabe
Snapshot-Kompatibilität Nicht gegeben – Snapshots verlieren ihre COW-Verbindung
Empfehlung Nur für Dateien ohne Snapshot-Bezug; bei Snapper-Systemen vermeiden

Für Systeme mit Snapper oder Timeshift gilt: Defragmentierung sollte vermieden werden, es sei denn, sie wird gezielt auf Dateien angewendet, die nachweislich nicht in Snapshots enthalten sind. Der Speicherplatzgewinn durch Snapshots wiegt den Performance-Verlust durch Fragmentierung in den meisten Fällen auf.

Defragmentieren mit Vorteilen & Risiko - Backup

Um dann doch defragmentieren zu können, wenn es wirklich notwendig sein sollte, wäre eine nicht wirklich komplett durchdachte Idee

  1. ein Backup zu erstellen
  2. danach alle Snapshots zu löschen
  3. defragmentieren
  4. neue Snapshots zu erstellen
  5. nochmal ein Backup erstellen

Fazit & Best Practice

BTRFS mit Snapshots ist eine praktische Möglichkeit, Systemänderungen abzusichern. Die manuellen Befehle sind überschaubar, und mit Snapper lässt sich der Prozess automatisieren. Für Systeme, auf denen häufig Updates oder Experimente durchgeführt werden, ist das eine sinnvolle Einrichtung. Ein Umstieg von ext4 ist nicht trivial, aber bei einer Neuinstallation oder einem Zweitsystem eine Überlegung wert.

Best Practice

  1. Standard-Layout mit @ und @home (oder wie vom Installer vorgegeben)
  2. Snapper für automatische Snapshots verwenden
  3. Vor kritischen Aktionen manuell einen Snapshot anlegen
  4. grub-btrfs installieren für snapper rollback, weil es die Snapshots ins GRUB-Menü einbindet.
  5. fstab-Optionen sparsam setzen
  6. Geschwindigkeit: Aktuelle Performancetests zeigen, dass BTRFS mit XFS, ext4 und anderen noch nicht mithalten kann, aka teilweise extrem langsamer ist. Wer Performance braucht, sollte aktuell auf ein anderes FS setzen.

Vorsicht

  1. nodatacow global setzen
  2. autodefrag in der fstab
  3. Defragmentierung auf Systemen mit Snapshots

Quellen

Offizielle Btrfs-Dokumentation

Snapper-Dokumentation

Distributionen und Praxisanleitungen

26. September 2026

Am Dienstag wird Mozilla Firefox 157 veröffentlichen und damit hält auch das neue Nova-Design in Firefox Einzug. Dieser Artikel gibt eine Vorschau auf das neue Firefox-Design, neue Funktionen und Nutzer-Optionen, die damit einhergehen, und geht auch auf Unterschiede im Vergleich zu den ersten Mockups ein.

Was ist „Nova”?

Am Dienstag, den 29. September 2026, wird Mozilla Firefox 157 veröffentlichen. Damit wird sich auch das Aussehen von Firefox verändern. Das neue Design hört intern auf den Projektnamen „Nova”. Anfang März hatte ich weltweit als erstes über das neue Design von Firefox berichtet.

Mozilla nimmt Abstand vom geplanten „Insel”-Design

Die ersten Mockups des neuen Nova-Designs konnte man durchaus als mutig bezeichnen. Denn die Abgrenzung einzelner Bereiche als eine Art von „Inseln” war eine drastische Abkehr vom gewohnten Erscheinungsbild, was einerseits modern und frisch wirkte, aber natürlich auch nicht jedem gefiel. Mozilla hatte dieses Konzept in Vorab-Versionen von Firefox auch schon implementiert. Aber insbesondere die Anforderungen an „Fitts’ Gesetz” stellten eine Herausforderung dar, weil es dadurch Lücken gab, in denen der Nutzer nicht „blind” mit der Oberfläche interagieren konnte. Während dies zwar ein technisch grundsätzlich lösbares Problem gewesen wäre, hat sich Mozilla dazu entschieden, von dieser Idee Abstand zu nehmen und es doch bei einer traditionelleren Darstellung zu belassen.

„Nova” bringt mehr Farbe und Rundungen in Firefox

Charakteristisch für das Nova-Design von Firefox sind vor allem die starken Rundungen: Tabs, Adressleiste und Schaltflächen sind sehr viel runder als bisher. Auch zahlreiche Icons wurden erneuert. Wo bislang Grau oder Blau als Akzentfarbe eingesetzt worden ist, setzt Mozilla nun auf Violett-Töne. Und während Flächen bislang einfarbig und graue Farben dominant waren, setzt Mozilla mit „Nova” auf mehr Farbe und teilweise auch auf dezente Verläufe.

Ein solcher Verlauf zeigt sich vor allem in der Tab-Leiste, welche von links nach rechts in einer subtilen Weise den Farbton verändert. Anders als in den ersten Mockups, in denen die Navigations- und Lesezeichenleiste eine weiße Fläche und damit einen starken Kontrast zur Tableiste gehabt hätten, ähnlich zum Grau im aktuellen Firefox-Design, zieht sich der Verlauf der Tab-Leiste auch über die Navigations- und Lesezeichen-Leiste, allerdings mit einer reduzierten Deckkraft im Vergleich zur Tab-Leiste. Dies sorgt weiterhin für eine Abgrenzung, aber ergibt ein stimmigeres und vor allem farbenfroheres Gesamtbild.

Einen Verlauf hat auch die Kontur um den aktiven Tab erhalten, welcher sich damit besser sichtbar von der Tab-Leiste abgrenzt, als es bisher der Fall war.

Firefox 157 „Nova”-Design

Verschiedene Themes für unterschiedliche Farbstimmungen

Mit „Nova” setzt Mozilla nicht auf ein einziges Farbschema, sondern gibt dem Anwender gleich zwölf verschiedene Themes, zwischen denen jederzeit gewechselt werden kann. Diese sind nicht nur im Themes-Bildschirm von about:addons auswählbar, sondern auch direkt über die Startseite. Neue Nutzer können den Farbton auch schon bei der Ersteinrichtung auswählen.

Firefox 157 „Nova”-Design Firefox 157 „Nova”-Design Firefox 157 „Nova”-Design

Die Auswahl eines Farbschemas beeinflusst auch die Hintergrundfarbe der Firefox-Startseite.

Firefox 157 „Nova”-Design Firefox 157 „Nova”-Design Firefox 157 „Nova”-Design Firefox 157 „Nova”-Design

Der Nutzer entscheidet, ob ein helles oder dunkles Theme genutzt wird

Ebenfalls neu und in den vorherigen Screenshots zu sehen: Überall, wo die Themes zur Auswahl stehen, kann der Nutzer jetzt auch direkt auswählen, ob er ein helles oder dunkles Theme nutzen möchte, was Auswirkungen auf Teile der Oberfläche wie Menü, Panels und die Startseite hat. Effektiv werden aus den zwölf standardmäßig inkludierten „Nova”-Themes damit 24 Farb-Optionen für den Nutzer, die Mozilla von Haus aus mitliefert. Aber auch mit anderen Themes funktioniert diese Option.

Firefox 157 „Nova”-Design Firefox 157 „Nova”-Design

Spezielle Farb-Option für Linux-Nutzer

Linux-Nutzer haben bei Verwendung des Standard-Themes von Firefox eine neue Option erhalten, um das Linux-Theme anstelle des Firefox-Themes zu verwenden. Dies wirkt sich auf die Farbgebung aus, insbesondere auf die Verwendung der violetten Akzent-Farbe an manchen Stellen der Oberfläche.

Firefox 157 „Nova”-Design

Neue Einstellung für Dichte der Oberfläche

Mit „Nova” bekommt auch ein früheres Feature sein Comeback: Eine Einstellung zur Dichte erlaubt es, den Abstand rund um Fensterelemente wie Symbolleiste, Tabs und Seitenleiste anzupassen. Während dies in den letzten Jahren nur über eine versteckte Option möglich war und nicht mehr offiziell unterstützt wurde, handelt es sich jetzt wieder um eine offiziell unterstützte Konfiguration, die sich in den Einstellungen unter „Erscheinungsbild” vornehmen lässt.

Neben dem automatischen Modus, der eine entsprechende Systemeinstellung berücksichtigt, stehen die Optionen Standard, Kompakt sowie Touch zur Verfügung. Bei kompakter Darstellung werden Elemente platzsparender angeordnet, während die Touch-Option für größere Flächen sorgt.

Firefox 157 „Nova”-Design Firefox 157 „Nova”-Design Firefox 157 „Nova”-Design

Icons für Vorschläge in Adressleiste

Bei Vorschlägen aus der Adressleiste überlagerte für Lesezeichen bereits ein Stern-Symbol das sogenannte Favicon der jeweiligen Website. Mit „Nova” zeigt Firefox auch für Vorschläge aus der Chronik ein Uhr-Symbol und für Seiten, die bereits als Tab geöffnet sind, ein Tab-Icon an.

Firefox 157 „Nova”-Design

Bessere Darstellung von Tab-Umgebungen in „Alle Tabs auflisten”-Menü

Das Menü „Alle Tabs auflisten” stellt Tab-Umgebungen mit „Nova” klarer dar, weil diese nicht länger nur durch einen Strich in Farbe der jeweiligen Tab-Umgebung dargestellt werden, sondern mit dem dazugehörigen Symbol.

Firefox 157 „Nova”-Design

Info-Schaltfläche für private Fenster

Überarbeitet wurde auch die Optik privater Fenster. Neben Farben und überarbeiteten Texten auf der Startseite wurde der Schriftzug „Privater Modus” am rechten Rand der Tab-Leiste durch ein Symbol ersetzt, auf dessen Klick eine Erklärung erscheint, was der Anwendungsfall für private Fenster ist.

Firefox 157 „Nova”-Design

Der Beitrag Große Vorschau auf das neue Nova-Design von Firefox 157 erschien zuerst auf soeren-hentzschel.at.

25. September 2026

Von Thundermail, dem E-Mail-Dienst der Thunderbird-Macher, gibt es auch eine Weboberfläche. Diese hat zahlreiche Neuerungen erhalten.

Das ist Thundermail

Thunderbird ist vor allem für seinen kostenlosen E-Mail-Client für Windows, macOS und Linux bekannt. Seit November 2024 gibt es Thunderbird auch für Android, Thunderbird für iOS ist in Entwicklung. Doch dabei soll es nicht bleiben: Die MZLA Technologies Corporation möchte ein Ökosystem aus Clients und Diensten als Alternative zu denen der Tech-Giganten wie Google Mail und Microsoft Office 365 etablieren, welches Open Source ist.

Unter dem Namen Thundermail bündelt MZLA ein neues Angebot, bestehend aus Postfach, Dienst zum Versand von Dateien sowie Tool zur Termin-Vereinbarung. Aktuell befindet sich Thundermail noch in einer Testphase, für welche man sich auf eine Warteliste setzen kann.

Vor etwas mehr als zwei Monaten wurde die erste Alpha-Version einer Weboberfläche für den E-Mail-Dienst gestartet.

Neuerungen für den Webmailer

Eben jene Weboberfläche hat zahlreiche Neuerungen erhalten, die sich in drei Kategorien aufteilen lassen: Postfach, Verfassen von E-Mails, Adressbuch.

Neuerungen im Postfach

Nachrichten können jetzt mit einem Stern markiert werden. In einem anderen Programm markierte Nachrichten wurden bereits entsprechend gekennzeichnet, jetzt funktioniert das Setzen und Entfernen der Markierung auch aus dem Webmailer heraus.

Über eine eigene Schaltfläche lassen sich Entwürfe jetzt als Tab minimieren.

Außerdem wurden die Tastatur-Kommandos überarbeitet. Dafür kann der Anwender aus zwei Voreinstellungen wählen: Kommandos, die konsistent mit den entsprechenden Befehlen in Thunderbird sind, oder Kommandos, die webkompatibler sind und nicht in Konflikt mit gängigen Browser-Kommandos stehen.

Neuerungen für das Schreiben von E-Mails

Das Fenster zum Schreiben von E-Mails hat eine Autovervollständigung für E-Mail-Adressen erhalten.

Außerdem lassen sich jetzt auch CC sowie BCC setzen.

Eine neue Schaltfläche zum Hochladen von Dateianhängen wurde hinzugefügt.

Darüber hinaus gibt es jetzt die Möglichkeit, eine E-Mail für den späteren Versand einzuplanen.

Neuerungen für das Adressbuch

Es können jetzt mehrere Adressbücher erstellt werden.

Für gelöschte Kontakte gibt es ab sofort einen eigenen Papierkorb.

Der Beitrag Webmail-Version von Thundermail erhält zahlreiche Neuerungen erschien zuerst auf soeren-hentzschel.at.

24. September 2026

Die MZLA Technologies Corporation hat mit Thunderbird 156.0.1 eine neue Version seines Open Source E-Mail-Clients für Windows, Apple macOS und Linux veröffentlicht.

Neuerungen von Thunderbird 156.0.1

Mit Thunderbird 156.0.1 hat die MZLA Technologies Corporation ein Update für seinen Open Source E-Mail-Client veröffentlicht. Die neue Version behebt eine potenzielle Absturzursache beim Speichern von Nachrichten.

Der Beitrag Thunderbird 156.0.1 veröffentlicht erschien zuerst auf soeren-hentzschel.at.

23. September 2026

Die MZLA Technologies Corporation hat mit Thunderbird 23.1 ein Update für die Android-Version seines E-Mail-Clients veröffentlicht.

Download Thunderbird für Android

Die MZLA Technologies Corporation hat Thunderbird 23.1 für Android veröffentlicht. Das Update behebt das Problem, dass Dateinamen von Anhängen mit Nicht-ASCII-Zeichen falsch dekodiert und gespeichert worden sind.

Der Beitrag Thunderbird 23.1 für Android veröffentlicht erschien zuerst auf soeren-hentzschel.at.

22. September 2026

Mozilla hat mit Firefox 156.0.1 ein Korrektur-Update veröffentlicht. Dieser Artikel beschreibt die Änderungen des neuesten Updates.

Download Mozilla Firefox 156.0.1

Unter macOS 27 wurden Fenster umgehend wieder maximiert, nachdem diese durch Doppelklick auf die Titelleiste auf ihre vorherige Größe gebracht werden sollten.

Elemente zeigten innerhalb eines Links beim Anklicken nicht ihre CSS-Stile für den :active-Zustand an.

Auf Websites, die eine CSS Anker-Positionierung verwenden, konnte es dazu kommen, dass Firefox nicht mehr reagierte.

Aufrufe von confirm(), alert() und prompt() funktionierten nicht mehr im Kontext von WebExtension-Pop-ups.

Die Screenreader-Software NVDA sagte die Schaltflächen in der Adressleiste nicht an, wenn der Mauszeiger darüber bewegt wurde.

Ein Freigabe-Problem von System-Handles beim Starten und Beenden von Inhaltsprozessen unter Windows wurde behoben.

Außerdem wurden zwei potenzielle Absturzursachen aus der Welt geschafft.

Der Beitrag Mozilla veröffentlicht Firefox 156.0.1 erschien zuerst auf soeren-hentzschel.at.

Installation NeoVim in Manjaro

Hinweis: Wenn du es wesentlich einfacher haben willst, aber dennoch das Vim oder Neovim Feeling nicht missen willst, dann kannst du auch diesen Rust Editor benutzen: Zed - Your last next editor - https://zed.dev/

Manjaros offizielle Repositories bieten in der Regel eine aktuelle Version von Neovim.

Mit pacman

sudo pacman -Syu
sudo pacman -S neovim lua51

Nach der Installation kannst du die Version mit folgendem Befehl prüfen:

nvim --version

Installation Rust

Was ist Rustup - Toolchain

Vorteil von Rustup

Rustup ist der offizielle Toolchain-Manager für Rust. Ohne Rustup installierst du Rust einmalig über die Paketverwaltung deiner Distribution und bist an diese eine Version gebunden. Rustup bietet darüber hinaus folgende Vorteile:

Mehrere Toolchains parallel

Du kannst gleichzeitig die stable, beta und nightly Version von Rust installieren und zwischen ihnen wechseln:

rustup install nightly
rustup install beta
rustup default stable
rustup override set nightly   # nur für aktuelles Projekt

Das ist besonders nützlich, da einige Crates (Programme oder Bibliotheken) z. B. für experimentelle Features nur auf nightly laufen.

Projektbezogene Versionen

Mit einer rust-toolchain.toml Datei im Projektordner legst du fest, welche Rust-Version das Projekt verwendet:

[toolchain]
channel = "1.75.0"
components = ["rustfmt", "clippy"]

Sobald du in den Ordner wechselst, aktiviert Rustup automatisch die passende Version. Das stellt sicher, dass alle Teammitglieder und CI-Systeme dieselbe Version verwenden.

Pinning auf exakte Versionen

Du kannst gezielt eine bestimmte Rust-Version installieren, um reproduzierbare Builds zu gewährleisten:

rustup install 1.75.0
rustup default 1.75.0

Komponenten nachinstallieren

Zusätzliche Werkzeuge wie rustfmt, clippy, rust-analyzer oder rust-src lassen sich jederzeit ergänzen oder entfernen:

rustup component add clippy
rustup component remove rust-docs

Target-Unterstützung

Für Cross-Compilation kannst du Ziele für andere Plattformen hinzufügen:

rustup target add aarch64-unknown-linux-gnu
rustup target add x86_64-pc-windows-gnu

Updates

Rustup aktualisiert die Toolchains mit einem Befehl:

rustup update

Im Gegensatz dazu hängst du bei der Installation über pacman von den Update-Zyklen der Distribution ab, die oft Monate hinterherhinken.

Vorteil einer Toolchain

Eine Toolchain ist das eigentliche Rust-Komplettpaket und besteht aus mehreren Komponenten:

  • rustc: der Compiler
  • cargo: der Build-Manager und Package-Manager
  • rust-std: die Standardbibliothek
  • rust-docs: die lokale Dokumentation
  • optionale Komponenten wie rustfmt und clippy

Warum das wichtig ist

Der Vorteil liegt darin, dass alle Komponenten einer Toolchain aufeinander abgestimmt sind. Der Compiler, die Standardbibliothek und die Werkzeuge stammen aus derselben Version. Das vermeidet Versionskonflikte, wie sie bei manuell zusammengestellten Installationen auftreten können.

Kanal statt Version

Eine Toolchain wird über einen Kanal referenziert (stable, beta, nightly) oder über eine exakte Version (1.75.0). Dadurch kannst du entweder immer die neuesten stabilen Features nutzen oder auf eine feste Version setzen, wenn dein Projekt das erfordert.

Vergleich: Rustup vs. Paketmanager

Aspekt Rustup pacman
Version Aktuell Oft veraltet
Mehrere Versionen Ja Nein
Kanalwechsel Ja Nein
Projektbezogene Versionen Ja Nein
Komponenten nachinstallieren Ja Teilweise
Cross-Compilation-Targets Ja Eingeschränkt
Update-Zyklus Unabhängig Abhängig von Distribution

Wann reicht der Paketmanager?

Wenn du Rust nur gelegentlich nutzt, keine projektbezogenen Versionen brauchst und mit der Version deiner Distribution zufrieden bist, ist die Installation über pacman ausreichend. Für die professionelle Entwicklung ist Rustup jedoch die klar bessere Wahl.

Rustup installieren

Schritt 1: base-devel & Rustup aus dem Repository installieren

sudo pacman -S base-devel rustup

Schritt 2: Toolchain aktivieren

rustup default stable

Wichtig: Das rustup-Paket aus den Arch-Repositories installiert standardmäßig keine Toolchain. Die Binaries (rustc, cargo) sind symbolische Links auf rustup, daher muss die Toolchain manuell aktiviert werden.

Schritt 3: Installation überprüfen

rustc --version
cargo --version

Entwicklungswerkzeuge

Nach der Installation von Rustup kannst du zusätzliche Komponenten hinzufügen:

rustup component add rustfmt         # Code-Formatierung
rustup component add clippy          # Statische Analyse
rustup component add rust-analyzer   # Language Server für IDEs

Rust Installation testen

cargo new hello_world
cd hello_world
cargo run

NeoVim 4 Rust

Um Rust in Neovim zu programmieren, brauchst du im Wesentlichen Rust selbst (erledigt) , den Language Server rust-analyzer und eine Neovim-LSP-Konfiguration.

LSP - Language Server rust-analyzer

  1. rust-analyzer (der LSP-Server): Das ist das Herzstück für Code-Vervollständigung, Fehlerprüfung und Navigation. Der beste Weg ist, ihn über rustup als Komponente hinzuzufügen .
  2. rustup component add rust-analyzer
    

Weil es hier und da Probleme mit dem Aufruf von rust-analyzer gibt, sollte noch folgende Anpassung gemacht werden.

Sollte der Aufruf nicht funktionieren

rust-analyzer --version

installiere explizit und danach rufe den oberen Befehle nochmal auf

sudo pacman -S rust-analyzer

Das Ganze hat mit einem kaputten Symlink/Proxy zu tun. Eventuell später mehr dazu. Es soll jetzt erstmal funktionieren!

Neovim Konfiguration

# Übersicht der Verzeichnisse

~/.config/nvim/
├── init.lua                          # Einstiegspunkt, lädt config.lazy
└── lua/
    ├── config/
    │   └── lazy.lua                  # Bootstrap & Setup für lazy.nvim
    └── plugins/
        ├── init.lua                  # Platzhalter, verhindert Fehler
        ├── rust.lua                  # rustaceanvim
        └── completion.lua            # blink.cmp
  1. Lege das Konfigurationsverzeichnis an
mkdir -p ~/.config/nvim
  1. Installiere Plugin Manager lazy.vim

Bevor du beginnst, stelle sicher, dass auf deinem System Neovim (>= 0.8.0) und Git (>= 2.19.0) installiert sind.

nvim --version | head -n 1 && git --version

Falls Git noch nicht installiert ist:

sudo pacman -S git
  1. Konfigurationsdateien anlegen

Erstelle zuerst die benötigten Ordner und Dateien in deinem Neovim-Konfigurationsverzeichnis.

mkdir -p ~/.config/nvim/lua/config && mkdir -p ~/.config/nvim/lua/plugins
  1. init.lua erstellen oder anpassen

Erstelle die Datei ~/.config/nvim/init.lua (falls sie noch nicht existiert) und füge diese Zeile ein:

nvim ~/.config/nvim/init.lua
require("config.lazy")
  1. Bootstrap-Datei für lazy.nvim erstellen

Damit lazy.nvim keine Fehlermeldung zurück gibt, muss in ~/.config/nvim/lua/plugins/ eine leere init.lua angelegt werden.

echo "return {}" > ~/.config/nvim/lua/plugins/init.lua
  1. Erstelle die Datei ~/.config/nvim/lua/config/lazy.lua
nvim ~/.config/nvim/lua/config/lazy.lua

mit folgendem Inhalt. Dieser Code in lazy.lua lädt das Plugin lazy.nvim automatisch herunter, falls es noch nicht vorhanden ist:

-- ~/.config/nvim/lua/config/lazy.lua

-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
  local lazyrepo = "https://github.com/folke/lazy.nvim.git"
  local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
  if vim.v.shell_error ~= 0 then
    vim.api.nvim_echo({
      { "Failed to clone lazy.nvim:\n", "ErrorMsg" },
      { out, "WarningMsg" },
      { "\nPress any key to exit..." },
    }, true, {})
    vim.fn.getchar()
    os.exit(1)
  end
end
vim.opt.rtp:prepend(lazypath)

-- Leader-Tasten müssen vor lazy.nvim gesetzt werden
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- lazy.nvim Setup
require("lazy").setup({
  spec = {
    { import = "plugins" },
  },

  -- LuaRocks-Unterstützung mit hererocks
  rocks = {
    enabled = true,
    hererocks = true,
  },

  -- Farbschema beim Installieren
  install = { colorscheme = { "habamax" } },

  -- Automatische Update-Prüfung
  checker = { enabled = true },

  -- Performance
  performance = {
    rtp = {
      disabled_plugins = {
        "gzip",
        "matchit",
        "matchparen",
        "netrwPlugin",
        "tarPlugin",
        "tohtml",
        "tutor",
        "zipPlugin",
      },
    },
  },
})
  1. Installation prüfen

Nachdem du die Konfiguration gespeichert hast, starte Neovim neu. Das Plugin lazy.nvim sollte sich nun automatisch installieren.

lazy.nvim öffnen: Gib den Befehl :Lazy ein, um die grafische Oberfläche zu öffnen.

Sobald lazy.nvim läuft, kannst du mit dem nächsten Schritt fortfahren: der Einrichtung von rustaceanvim in deiner Plugin-Konfiguration.

  1. Plugin rustaceanvim installieren

Für die Konfiguration gibt es zwei gängige Wege. Der einfachste und modernste Weg ist ein spezielles Plugin für Rust.

Dieses Plugin ist eine Art “Rundum-sorglos-Paket” für Rust in Neovim. Es verbindet sich automatisch mit rust-analyzer und bringt viele nützliche Funktionen mit, ohne dass du den LSP-Server manuell konfigurieren musst .

Der rustaceanvim-Eintrag kommt nicht in die lazy.lua, sondern in eine separate Datei im plugins-Ordner. Die lazy.lua ist nur für den Bootstrap und die globale Konfiguration von lazy.nvim zuständig. Deine eigentlichen Plugins gehören in ~/.config/nvim/lua/plugins/.

Erstelle eine neue Datei:

nvim ~/.config/nvim/lua/plugins/rust.lua

Die Datei muss eine Tabelle zurückgeben. Der rustaceanvim-Eintrag steht in dieser Tabelle:

-- ~/.config/nvim/lua/plugins/rust.lua
return {
  {
    "mrcjkb/rustaceanvim",
    version = "^9", -- Version 9 für aktuelle Neovim-Versionen
    lazy = false,   -- Wichtig: Lädt das Plugin sofort
  },
}
  1. Speichere die Datei.
  2. Starte Neovim neu.
  3. gib :Lazy ein und eventuell U zum Updaten
  4. lazy.nvim erkennt die neue Datei und installiert rustaceanvim automatisch.
  5. Öffne eine .rs-Datei. rustaceanvim startet dann rust-analyzer.

Plugin blink.cmp - autocompletion

Installation

Erstelle folgende Datei :

nvim ~/.config/nvim/lua/plugins/completion.lua

Mit folgendem Inhalt

return {
  {
    'saghen/blink.cmp',
    -- Optional: Stellt Snippets für die Snippet-Vervollständigung bereit
    dependencies = { 'rafamadriz/friendly-snippets' },

    -- Nutzt einen Release-Tag, um vorgebaute Binärdateien herunterzuladen.
    -- Das ist der empfohlene Weg für die beste Performance.
    version = '1.*',

    ---@module 'blink.cmp'
    ---@type blink.cmp.Config
    opts = {
      -- 'default' verwendet Tastenkürzel, die denen der eingebauten
      -- Vervollständigung ähneln (z.B.  zum Akzeptieren)
      keymap = { preset = 'default' },

      appearance = {
        -- 'mono' für 'Nerd Font Mono', um Icons korrekt auszurichten
        nerd_font_variant = 'mono'
      },

      -- Standardmäßig werden Vorschläge von LSP, Dateipfaden,
      -- Snippets und dem aktuellen Buffer verwendet.
      sources = {
        default = { 'lsp', 'path', 'snippets', 'buffer' },
      },

      -- Nutzt die Rust-Implementierung für den Fuzzy-Matcher (schneller),
      -- fällt aber automatisch auf die Lua-Implementierung zurück.
      fuzzy = { implementation = "prefer_rust_with_warning" }
    },
    opts_extend = { "sources.default" }
  }
}

Shortcuts

blink.cmp verwendet standardmäßig das default-Preset, das sich an der eingebauten Neovim-Vervollständigung orientiert . Die wichtigsten Tastenkürzel im Insert-Modus sind:

  • : Menü öffnen oder Dokumentation umschalten .
  • : Ausgewählten Vorschlag akzeptieren .
  • : Menü schließen (und Vorschau rückgängig machen) .
  • / (oder Pfeil runter/hoch): Nächsten oder vorherigen Eintrag auswählen .
  • : Signatur-Hilfe umschalten (sofern aktiviert) .
  • / : In der Dokumentationsvorschau scrollen .
  • / : Zwischen Snippet-Platzhaltern springen .

Möchtest du ein anderes Verhalten, kannst du in deiner blink.cmp-Konfiguration einfach ein anderes Preset wählen, z. B. keymap = { preset = 'super-tab' } für Tab-zum-Akzeptieren .

Neben den von mir genannten Shortcuts gibt es bei blink.cmp noch einige weitere, die je nach Konfiguration und Kontext (z.B. in der Kommandozeile) verfügbar sind.

Presets

Presets sind bei blink.cmp vorgefertigte Tastaturbelegungen, die festlegen, wie du das Vervollständigungsmenü bedienst. Sie sind sozusagen „Pakete“ von Tastenkürzeln für bestimmte Arbeitsweisen.

Die wichtigsten Presets im Überblick

Du wählst ein Preset über keymap = { preset = 'name' } in deiner Konfiguration .

  • default: Orientiert sich an der eingebauten Neovim-Vervollständigung. Du bestätigst einen Vorschlag mit (wie „Yes“) .
  • super-tab: Orientiert sich an VS Code. Die Tab-Taste akzeptiert den ausgewählten Vorschlag .
  • enter: Verwendet die Enter-Taste () zum Akzeptieren. Die Tab-Taste bleibt für die Navigation zwischen Snippet-Platzhaltern reserviert .
  • cmdline: Ist speziell für die Kommandozeile (:) optimiert. Die Tab-Taste zeigt das Menü, fügt den ersten Eintrag ein oder wählt den nächsten aus .

Gemeinsame Tasten in allen Presets

Unabhängig vom gewählten Preset sind einige Tasten immer gleich belegt, zum Beispiel :

  • : Menü öffnen oder Dokumentation umschalten.
  • / (oder Pfeiltasten): Nächsten oder vorherigen Eintrag auswählen.
  • : Menü schließen.

Die wichtigsten Presets im Vergleich

blink.cmp bringt verschiedene vordefinierte Tastaturbelegungen mit, die sogenannten Presets. Deine aktuell genutzten Shortcuts (, , etc.) gehören zum default-Preset. Andere Presets ändern die Belegung grundlegend:

Funktion default super-tab enter cmdline
Akzeptieren (smart) (Enter)
Nächstes , , , ,
Vorheriges , , , ,
Snippet vorwärts (smart) —

Wenn du also z.B. möchtest, dass Tab den Vorschlag akzeptiert (wie in VS Code), änderst du keymap = { preset = 'super-tab' } in deiner Konfiguration .

Weitere nützliche Standard-Shortcuts

Unabhängig vom Preset gibt es einige Befehle, die in den meisten Konfigurationen zu finden sind:

  • : Schaltet die Signatur-Hilfe um (sofern aktiviert) .
  • / : Scrollt in der Dokumentationsvorschau nach oben/unten .
  • : Zeigt das Vervollständigungsmenü oder die Dokumentation an .
  • : Schließt das Menü (und macht eine automatische Vorschau rückgängig) .

Shortcuts in der Kommandozeile (cmdline)

Wenn du in der Neovim-Kommandozeile (:) tippst, gelten oft andere Regeln. Das cmdline-Preset ist speziell dafür optimiert :

  • : Zeigt das Menü, fügt das erste Element ein oder wählt das nächste aus.
  • : Wählt das vorherige Element aus.
  • / : Navigiert wie in der normalen Vervollständigung durch die Vorschläge.

Eigene Shortcuts definieren

Du kannst die Belegung jederzeit anpassen. Wenn du zum Beispiel (Enter) zum Akzeptieren nutzen möchtest, änderst du in deiner blink.cmp-Konfiguration den Eintrag keymap:

keymap = { preset = 'enter' },

Oder du definierst komplett eigene Kombinationen, indem du ein preset = 'none' setzt und die Tasten selbst belegst .

Beispiel:

-- ~/.config/nvim/lua/plugins/completion.lua
return {
  {
    'saghen/blink.cmp',
    dependencies = { 'rafamadriz/friendly-snippets' },
    version = '1.*',
    opts = {
      keymap = { preset = 'enter' }, -- Hier wird das Preset gewählt
      -- ... restliche Optionen wie appearance, sources, fuzzy
    },
  },
}

Installation Script für Neovim, Rust und Plugins unter Manjaro

Für Ungeduldige mit Risikobereitschaft: Hier ist ein vollständiges Bash-Skript, das alle Schritte aus deinem Leitfaden automatisiert. Es installiert Neovim, Rust (via Rustup), die Rust-Komponenten und richtet die komplette Neovim-Konfiguration mit lazy.nvim, rustaceanvim und blink.cmp ein.

#!/usr/bin/env bash
### ### install_neovim_rust.sh
### Installiert Neovim, Rust (Rustup) und richtet die Neovim-Konfiguration
### mit lazy.nvim, rustaceanvim und blink.cmp unter Manjaro ein.
### ### Autor: hoergen (Vorlage), automatisiert
### Datum: 2026-09-22
### 
set -euo pipefail

### ------------------------------------------------------------------
### Farben für Ausgaben
### ------------------------------------------------------------------
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color

info()  { echo -e "${GREEN}[INFO]${NC}  $*"; }
warn()  { echo -e "${YELLOW}[WARN]${NC}  $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }

### ------------------------------------------------------------------
### 0. Voraussetzungen prüfen
### ------------------------------------------------------------------
if [[ $EUID -eq 0 ]]; then
  error "Bitte NICHT als root ausführen. Das Skript nutzt sudo wo nötig."
  exit 1
fi

if ! command -v pacman &>/dev/null; then
  error "pacman nicht gefunden. Dieses Skript ist für Manjaro/Arch gedacht."
  exit 1
fi

### ------------------------------------------------------------------
### 1. System aktualisieren und Neovim + Lua installieren
### ------------------------------------------------------------------
info "Aktualisiere Paketdatenbank und System ..."
sudo pacman -Syu --noconfirm

info "Installiere Neovim, Lua 5.1, git, base-devel und rustup ..."
sudo pacman -S --noconfirm --needed \
  neovim \
  lua51 \
  git \
  base-devel \
  rustup

### ------------------------------------------------------------------
### 2. Rust-Toolchain aktivieren
### ------------------------------------------------------------------
info "Aktiviere Rust stable Toolchain ..."
if ! rustup toolchain list | grep -q '^stable'; then
  rustup default stable
else
  info "stable Toolchain ist bereits aktiv."
fi

### ------------------------------------------------------------------
### 3. Rust-Komponenten installieren
### ------------------------------------------------------------------
info "Installiere Rust-Komponenten: rustfmt, clippy, rust-analyzer ..."
rustup component add rustfmt   || warn "rustfmt konnte nicht installiert werden."
rustup component add clippy    || warn "clippy konnte nicht installiert werden."
rustup component add rust-analyzer || warn "rust-analyzer (rustup) konnte nicht installiert werden."

### Fallback: rust-analyzer aus den Repos, falls der rustup-Aufruf nicht klappt.
if ! command -v rust-analyzer &>/dev/null; then
  warn "rust-analyzer nicht im PATH gefunden. Installiere aus den Repos ..."
  sudo pacman -S --noconfirm --needed rust-analyzer
fi

### ------------------------------------------------------------------
### 4. Versionen prüfen
### ------------------------------------------------------------------
info "Überprüfe Installationen ..."
nvim --version | head -n 1
rustc --version
cargo --version
rust-analyzer --version 2>/dev/null || warn "rust-analyzer --version nicht verfügbar."

### ------------------------------------------------------------------
### 5. Neovim-Konfigurationsverzeichnis anlegen
### ------------------------------------------------------------------
NVIM_CONFIG="$HOME/.config/nvim"
NVIM_LUA="$NVIM_CONFIG/lua"
NVIM_PLUGINS="$NVIM_LUA/plugins"

info "Lege Neovim-Konfigurationsverzeichnisse an ..."
mkdir -p "$NVIM_CONFIG"
mkdir -p "$NVIM_LUA/config"
mkdir -p "$NVIM_PLUGINS"

### ------------------------------------------------------------------
### 6. init.lua erstellen
### ------------------------------------------------------------------
info "Erstelle $NVIM_CONFIG/init.lua ..."
cat > "$NVIM_CONFIG/init.lua" <<'EOF'
require("config.lazy")
EOF

### ------------------------------------------------------------------
### 7. Plugin-Init (leer, damit lazy.nvim keine Fehler wirft)
### ------------------------------------------------------------------
info "Erstelle $NVIM_PLUGINS/init.lua ..."
cat > "$NVIM_PLUGINS/init.lua" <<'EOF'
return {}
EOF

### ------------------------------------------------------------------
### 8. lazy.lua Bootstrap-Datei erstellen
### ------------------------------------------------------------------
info "Erstelle $NVIM_LUA/config/lazy.lua ..."
cat > "$NVIM_LUA/config/lazy.lua" <<'EOF'
-- ~/.config/nvim/lua/config/lazy.lua

-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
  local lazyrepo = "https://github.com/folke/lazy.nvim.git"
  local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
  if vim.v.shell_error ~= 0 then
    vim.api.nvim_echo({
      { "Failed to clone lazy.nvim:\n", "ErrorMsg" },
      { out, "WarningMsg" },
      { "\nPress any key to exit..." },
    }, true, {})
    vim.fn.getchar()
    os.exit(1)
  end
end
vim.opt.rtp:prepend(lazypath)

-- Leader-Tasten müssen vor lazy.nvim gesetzt werden
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- lazy.nvim Setup
require("lazy").setup({
  spec = {
    { import = "plugins" },
  },

  -- LuaRocks-Unterstützung mit hererocks
  rocks = {
    enabled = true,
    hererocks = true,
  },

  -- Farbschema beim Installieren
  install = { colorscheme = { "habamax" } },

  -- Automatische Update-Prüfung
  checker = { enabled = true },

  -- Performance
  performance = {
    rtp = {
      disabled_plugins = {
        "gzip",
        "matchit",
        "matchparen",
        "netrwPlugin",
        "tarPlugin",
        "tohtml",
        "tutor",
        "zipPlugin",
      },
    },
  },
})
EOF

### ------------------------------------------------------------------
### 9. rustaceanvim Plugin-Konfiguration
### ------------------------------------------------------------------
info "Erstelle $NVIM_PLUGINS/rust.lua ..."
cat > "$NVIM_PLUGINS/rust.lua" <<'EOF'
-- ~/.config/nvim/lua/plugins/rust.lua
return {
  {
    "mrcjkb/rustaceanvim",
    version = "^9", -- Version 9 für aktuelle Neovim-Versionen
    lazy = false,   -- Wichtig: Lädt das Plugin sofort
  },
}
EOF

### ------------------------------------------------------------------
### 10. blink.cmp Plugin-Konfiguration
### ------------------------------------------------------------------
info "Erstelle $NVIM_PLUGINS/completion.lua ..."
cat > "$NVIM_PLUGINS/completion.lua" <<'EOF'
-- ~/.config/nvim/lua/plugins/completion.lua
return {
  {
    'saghen/blink.cmp',
    -- Optional: Stellt Snippets für die Snippet-Vervollständigung bereit
    dependencies = { 'rafamadriz/friendly-snippets' },

    -- Nutzt einen Release-Tag, um vorgebaute Binärdateien herunterzuladen.
    -- Das ist der empfohlene Weg für die beste Performance.
    version = '1.*',

    ---@module 'blink.cmp'
    ---@type blink.cmp.Config
    opts = {
      -- 'default' verwendet Tastenkürzel, die denen der eingebauten
      -- Vervollständigung ähneln (z.B.  zum Akzeptieren)
      keymap = { preset = 'default' },

      appearance = {
        -- 'mono' für 'Nerd Font Mono', um Icons korrekt auszurichten
        nerd_font_variant = 'mono'
      },

      -- Standardmäßig werden Vorschläge von LSP, Dateipfaden,
      -- Snippets und dem aktuellen Buffer verwendet.
      sources = {
        default = { 'lsp', 'path', 'snippets', 'buffer' },
      },

      -- Nutzt die Rust-Implementierung für den Fuzzy-Matcher (schneller),
      -- fällt aber automatisch auf die Lua-Implementierung zurück.
      fuzzy = { implementation = "prefer_rust_with_warning" }
    },
    opts_extend = { "sources.default" }
  }
}
EOF

### ------------------------------------------------------------------
### 11. Plugins headless installieren (lazy.nvim sync)
### ------------------------------------------------------------------
info "Installiere Plugins über lazy.nvim (headless) ..."
if nvim --headless "+Lazy! sync" +qa 2>/dev/null; then
  info "Plugins erfolgreich synchronisiert."
else
  warn "Headless-Sync fehlgeschlagen. Bitte Neovim manuell starten und ':Lazy sync' ausführen."
fi

### ------------------------------------------------------------------
### 12. Fertig
### ------------------------------------------------------------------
info "Fertig! Zusammenfassung:"
echo "  - Neovim-Version:       $(nvim --version | head -n 1)"
echo "  - Rust-Version:         $(rustc --version)"
echo "  - Cargo-Version:        $(cargo --version)"
echo "  - rust-analyzer:        $(rust-analyzer --version 2>/dev/null || echo 'nicht verfügbar')"
echo "  - Neovim-Konfig:        $NVIM_CONFIG"
echo
info "Starte Neovim mit 'nvim' und prüfe mit ':Lazy', ob alle Plugins installiert sind."
info "Öffne eine .rs-Datei, um rustaceanvim und rust-analyzer zu testen."

Verwendung

  1. Datei speichern als z. B. install_neovim_rust.sh.

  2. Ausführbar machen:

    chmod +x install_neovim_rust.sh
    
  3. Starten (nicht als root):

    ./install_neovim_rust.sh
    

Was das Skript macht

Schritt Aktion
1 System-Update via pacman -Syu
2 Installation von neovim, lua51, git, base-devel, rustup
3 Aktivierung der Rust-stable-Toolchain
4 Hinzufügen von rustfmt, clippy, rust-analyzer
5 Fallback: rust-analyzer aus den Repos, falls rustup-Version nicht im PATH
6 Anlegen der Verzeichnisse ~/.config/nvim/lua/{config,plugins}
7 Erstellen von init.lua mit require("config.lazy")
8 Erstellen der leeren plugins/init.lua
9 Erstellen der config/lazy.lua (Bootstrap + Setup)
10 Erstellen der plugins/rust.lua (rustaceanvim)
11 Erstellen der plugins/completion.lua (blink.cmp, Preset default)
12 Headless-Installation der Plugins via nvim --headless "+Lazy! sync" +qa

Hinweise

  • Das Skript verwendet bewusst das default-Preset für blink.cmp. Die Custom-Shortcuts (z. B. super-tab oder enter) sind nicht enthalten – du kannst sie später in ~/.config/nvim/lua/plugins/completion.lua anpassen.
  • rust-analyzer wird zuerst über rustup installiert. Falls das fehlschlägt oder der Befehl nicht im PATH landet, wird automatisch das Paket aus den Manjaro-Repos nachinstalliert.
  • Die Plugin-Installation läuft headless. Sollte das fehlschlagen, kannst du Neovim einfach normal starten und :Lazy sync ausführen.

Installing NeoVim on Manjaro

Note: If you want it to be considerably simpler, but still don’t want to miss the Vim or Neovim feeling, then you can also use this Rust editor: Zed - Your last next editor - https://zed.dev/

Manjaro’s official repositories usually offer a current version of Neovim.

With pacman

sudo pacman -Syu
sudo pacman -S neovim lua51

After installation, you can check the version with the following command:

nvim --version

Installing Rust

What Is Rustup - Toolchain

Advantage of Rustup

Rustup is the official toolchain manager for Rust. Without Rustup, you install Rust once via your distribution’s package management and are tied to that one version. Rustup additionally offers the following advantages:

Multiple Toolchains in Parallel

You can install the stable, beta, and nightly versions of Rust at the same time and switch between them:

rustup install nightly
rustup install beta
rustup default stable
rustup override set nightly   # only for the current project

This is particularly useful, since some crates (programs or libraries) only run on nightly, for example for experimental features.

Project-Specific Versions

With a rust-toolchain.toml file in the project folder, you define which Rust version the project uses:

[toolchain]
channel = "1.75.0"
components = ["rustfmt", "clippy"]

As soon as you switch into the folder, Rustup automatically activates the matching version. This ensures that all team members and CI systems use the same version.

Pinning to Exact Versions

You can specifically install a particular Rust version to ensure reproducible builds:

rustup install 1.75.0
rustup default 1.75.0

Installing Additional Components

Additional tools such as rustfmt, clippy, rust-analyzer, or rust-src can be added or removed at any time:

rustup component add clippy
rustup component remove rust-docs

Target Support

For cross-compilation, you can add targets for other platforms:

rustup target add aarch64-unknown-linux-gnu
rustup target add x86_64-pc-windows-gnu

Updates

Rustup updates the toolchains with one command:

rustup update

In contrast, with installation via pacman you depend on the distribution’s update cycles, which often lag months behind.

Advantage of a Toolchain

A toolchain is the actual complete Rust package and consists of several components:

  • rustc: the compiler
  • cargo: the build manager and package manager
  • rust-std: the standard library
  • rust-docs: the local documentation
  • optional components such as rustfmt and clippy

Why This Is Important

The advantage lies in the fact that all components of a toolchain are coordinated with one another. The compiler, the standard library, and the tools come from the same version. This avoids version conflicts such as can occur with manually assembled installations.

Channel Instead of Version

A toolchain is referenced via a channel (stable, beta, nightly) or via an exact version (1.75.0). This allows you to either always use the latest stable features or to rely on a fixed version when your project requires it.

Comparison: Rustup vs. Package Manager

Aspect Rustup pacman
Version Current Often outdated
Multiple versions Yes No
Channel switching Yes No
Project-specific versions Yes No
Installing additional components Yes Partially
Cross-compilation targets Yes Limited
Update cycle Independent Dependent on distribution

When Is the Package Manager Enough?

If you only use Rust occasionally, do not need project-specific versions, and are satisfied with your distribution’s version, installation via pacman is sufficient. For professional development, however, Rustup is clearly the better choice.

Installing Rustup

Step 1: Install base-devel & Rustup from the repository

sudo pacman -S base-devel rustup

Step 2: Activate the toolchain

rustup default stable

Important: The rustup package from the Arch repositories does not install a toolchain by default. The binaries (rustc, cargo) are symbolic links to rustup, so the toolchain must be activated manually.

Step 3: Verify the installation

rustc --version
cargo --version

Development Tools

After installing Rustup, you can add additional components:

rustup component add rustfmt         # Code formatting
rustup component add clippy          # Static analysis
rustup component add rust-analyzer   # Language server for IDEs

Testing the Rust Installation

cargo new hello_world
cd hello_world
cargo run

NeoVim 4 Rust

To program Rust in Neovim, you essentially need Rust itself (done), the language server rust-analyzer, and a Neovim LSP configuration.

LSP - Language Server rust-analyzer

  1. rust-analyzer (the LSP server): This is the heart of code completion, error checking, and navigation. The best way is to add it as a component via rustup.
  2. rustup component add rust-analyzer
    

Because there are occasional problems with invoking rust-analyzer, the following adjustment should also be made.

If the invocation does not work

rust-analyzer --version

install explicitly and then call the above command again

sudo pacman -S rust-analyzer

All of this has to do with a broken symlink/proxy. Possibly more on that later. For now, it should just work!

Neovim Configuration

# Overview of the directories

~/.config/nvim/
├── init.lua                          # Entry point, loads config.lazy
└── lua/
    ├── config/
    │   └── lazy.lua                  # Bootstrap & setup for lazy.nvim
    └── plugins/
        ├── init.lua                  # Placeholder, prevents errors
        ├── rust.lua                  # rustaceanvim
        └── completion.lua            # blink.cmp
  1. Create the configuration directory
mkdir -p ~/.config/nvim
  1. Install the plugin manager lazy.vim

Before you begin, make sure that Neovim (>= 0.8.0) and Git (>= 2.19.0) are installed on your system.

nvim --version | head -n 1 && git --version

If Git is not yet installed:

sudo pacman -S git
  1. Create configuration files

First create the required folders and files in your Neovim configuration directory.

mkdir -p ~/.config/nvim/lua/config && mkdir -p ~/.config/nvim/lua/plugins
  1. Create or adjust init.lua

Create the file ~/.config/nvim/init.lua (if it does not yet exist) and add this line:

nvim ~/.config/nvim/init.lua
require("config.lazy")
  1. Create bootstrap file for lazy.nvim

So that lazy.nvim does not return an error message, an empty init.lua must be created in ~/.config/nvim/lua/plugins/.

echo "return {}" > ~/.config/nvim/lua/plugins/init.lua
  1. Create the file ~/.config/nvim/lua/config/lazy.lua
nvim ~/.config/nvim/lua/config/lazy.lua

with the following content. This code in lazy.lua automatically downloads the plugin lazy.nvim if it is not yet present:

-- ~/.config/nvim/lua/config/lazy.lua

-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
  local lazyrepo = "https://github.com/folke/lazy.nvim.git"
  local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
  if vim.v.shell_error ~= 0 then
    vim.api.nvim_echo({
      { "Failed to clone lazy.nvim:\n", "ErrorMsg" },
      { out, "WarningMsg" },
      { "\nPress any key to exit..." },
    }, true, {})
    vim.fn.getchar()
    os.exit(1)
  end
end
vim.opt.rtp:prepend(lazypath)

-- Leader keys must be set before lazy.nvim
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- lazy.nvim setup
require("lazy").setup({
  spec = {
    { import = "plugins" },
  },

  -- LuaRocks support with hererocks
  rocks = {
    enabled = true,
    hererocks = true,
  },

  -- Colorscheme when installing
  install = { colorscheme = { "habamax" } },

  -- Automatic update check
  checker = { enabled = true },

  -- Performance
  performance = {
    rtp = {
      disabled_plugins = {
        "gzip",
        "matchit",
        "matchparen",
        "netrwPlugin",
        "tarPlugin",
        "tohtml",
        "tutor",
        "zipPlugin",
      },
    },
  },
})
  1. Verify the installation

After you have saved the configuration, restart Neovim. The plugin lazy.nvim should now install itself automatically.

Open lazy.nvim: Enter the command :Lazy to open the graphical interface.

Once lazy.nvim is running, you can proceed to the next step: setting up rustaceanvim in your plugin configuration.

  1. Install the plugin rustaceanvim

There are two common ways to configure this. The simplest and most modern way is a special plugin for Rust.

This plugin is a kind of “all-inclusive package” for Rust in Neovim. It automatically connects to rust-analyzer and brings many useful functions without you having to configure the LSP server manually.

The rustaceanvim entry does not go into lazy.lua, but into a separate file in the plugins folder. The lazy.lua is only responsible for the bootstrap and global configuration of lazy.nvim. Your actual plugins belong in ~/.config/nvim/lua/plugins/.

Create a new file:

nvim ~/.config/nvim/lua/plugins/rust.lua

The file must return a table. The rustaceanvim entry is in this table:

-- ~/.config/nvim/lua/plugins/rust.lua
return {
  {
    "mrcjkb/rustaceanvim",
    version = "^9", -- Version 9 for current Neovim versions
    lazy = false,   -- Important: Loads the plugin immediately
  },
}
  1. Save the file.
  2. Restart Neovim.
  3. Enter :Lazy and possibly U to update
  4. lazy.nvim recognizes the new file and installs rustaceanvim automatically.
  5. Open a .rs file. rustaceanvim then starts rust-analyzer.

Plugin blink.cmp - autocompletion

Installation

Create the following file:

nvim ~/.config/nvim/lua/plugins/completion.lua

With the following content

return {
  {
    'saghen/blink.cmp',
    -- Optional: Provides snippets for snippet completion
    dependencies = { 'rafamadriz/friendly-snippets' },

    -- Uses a release tag to download prebuilt binaries.
    -- This is the recommended way for the best performance.
    version = '1.*',

    ---@module 'blink.cmp'
    ---@type blink.cmp.Config
    opts = {
      -- 'default' uses key bindings similar to those of the built-in
      -- completion (e.g.,  to accept)
      keymap = { preset = 'default' },

      appearance = {
        -- 'mono' for 'Nerd Font Mono' to align icons correctly
        nerd_font_variant = 'mono'
      },

      -- By default, suggestions from LSP, file paths,
      -- snippets, and the current buffer are used.
      sources = {
        default = { 'lsp', 'path', 'snippets', 'buffer' },
      },

      -- Uses the Rust implementation for the fuzzy matcher (faster),
      -- but automatically falls back to the Lua implementation.
      fuzzy = { implementation = "prefer_rust_with_warning" }
    },
    opts_extend = { "sources.default" }
  }
}

Shortcuts

blink.cmp uses the default preset by default, which is based on the built-in Neovim completion. The most important key bindings in insert mode are:

  • : Open the menu or toggle documentation.
  • : Accept the selected suggestion.
  • : Close the menu (and undo the preview).
  • / (or arrow down/up): Select the next or previous entry.
  • : Toggle signature help (if enabled).
  • / : Scroll in the documentation preview.
  • / : Jump between snippet placeholders.

If you want different behavior, you can simply choose a different preset in your blink.cmp configuration, e.g., keymap = { preset = 'super-tab' } for tab-to-accept.

In addition to the shortcuts I mentioned, blink.cmp has a few more that are available depending on the configuration and context (e.g., in the command line).

Presets

In blink.cmp, presets are pre-made keyboard layouts that determine how you operate the completion menu. They are, so to speak, “packages” of key bindings for specific ways of working.

The Most Important Presets at a Glance

You choose a preset via keymap = { preset = 'name' } in your configuration.

  • default: Based on the built-in Neovim completion. You confirm a suggestion with (like “Yes”).
  • super-tab: Based on VS Code. The Tab key accepts the selected suggestion.
  • enter: Uses the Enter key () to accept. The Tab key remains reserved for navigating between snippet placeholders.
  • cmdline: Is specially optimized for the command line (:). The Tab key shows the menu, inserts the first entry, or selects the next one.

Common Keys in All Presets

Regardless of the chosen preset, some keys are always assigned the same way, for example:

  • : Open the menu or toggle documentation.
  • / (or arrow keys): Select the next or previous entry.
  • : Close the menu.

The Most Important Presets Compared

blink.cmp comes with various predefined keyboard layouts, the so-called presets. Your currently used shortcuts (, , , etc.) belong to the default preset. Other presets change the assignment fundamentally:

Function default super-tab enter cmdline
Accept (smart) (Enter)
Next , , , ,
Previous , , , ,
Snippet forward (smart) —

So if, for example, you want Tab to accept the suggestion (as in VS Code), you change keymap = { preset = 'super-tab' } in your configuration.

Other Useful Standard Shortcuts

Regardless of the preset, there are some commands that are found in most configurations:

  • : Toggles signature help (if enabled).
  • / : Scrolls up/down in the documentation preview.
  • : Shows the completion menu or the documentation.
  • : Closes the menu (and undoes an automatic preview).

Shortcuts in the Command Line (cmdline)

When you type in the Neovim command line (:), different rules often apply. The cmdline preset is specially optimized for this:

  • : Shows the menu, inserts the first element, or selects the next one.
  • : Selects the previous element.
  • / : Navigates through the suggestions as in normal completion.

Defining Your Own Shortcuts

You can adjust the assignment at any time. If, for example, you want to use (Enter) to accept, you change the keymap entry in your blink.cmp configuration:

keymap = { preset = 'enter' },

Or you define completely custom combinations by setting preset = 'none' and assigning the keys yourself.

Example:

-- ~/.config/nvim/lua/plugins/completion.lua
return {
  {
    'saghen/blink.cmp',
    dependencies = { 'rafamadriz/friendly-snippets' },
    version = '1.*',
    opts = {
      keymap = { preset = 'enter' }, -- Here the preset is chosen
      -- ... remaining options such as appearance, sources, fuzzy
    },
  },
}

Installation Script for Neovim, Rust, and Plugins on Manjaro

For the impatient with a willingness to take risks: Here is a complete Bash script that automates all steps from your guide. It installs Neovim, Rust (via Rustup), the Rust components, and sets up the complete Neovim configuration with lazy.nvim, rustaceanvim, and blink.cmp.

#!/usr/bin/env bash
### ### install_neovim_rust.sh
### Installs Neovim, Rust (Rustup) and sets up the Neovim configuration
### with lazy.nvim, rustaceanvim, and blink.cmp on Manjaro.
### ### Author: hoergen (template), automated
### Date: 2026-09-22
### 
set -euo pipefail

### ------------------------------------------------------------------
### Colors for output
### ------------------------------------------------------------------
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color

info()  { echo -e "${GREEN}[INFO]${NC}  $*"; }
warn()  { echo -e "${YELLOW}[WARN]${NC}  $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }

### ------------------------------------------------------------------
### 0. Check prerequisites
### ------------------------------------------------------------------
if [[ $EUID -eq 0 ]]; then
  error "Please do NOT run as root. The script uses sudo where necessary."
  exit 1
fi

if ! command -v pacman &>/dev/null; then
  error "pacman not found. This script is intended for Manjaro/Arch."
  exit 1
fi

### ------------------------------------------------------------------
### 1. Update system and install Neovim + Lua
### ------------------------------------------------------------------
info "Updating package database and system ..."
sudo pacman -Syu --noconfirm

info "Installing Neovim, Lua 5.1, git, base-devel, and rustup ..."
sudo pacman -S --noconfirm --needed \
  neovim \
  lua51 \
  git \
  base-devel \
  rustup

### ------------------------------------------------------------------
### 2. Activate Rust toolchain
### ------------------------------------------------------------------
info "Activating Rust stable toolchain ..."
if ! rustup toolchain list | grep -q '^stable'; then
  rustup default stable
else
  info "stable toolchain is already active."
fi

### ------------------------------------------------------------------
### 3. Install Rust components
### ------------------------------------------------------------------
info "Installing Rust components: rustfmt, clippy, rust-analyzer ..."
rustup component add rustfmt   || warn "rustfmt could not be installed."
rustup component add clippy    || warn "clippy could not be installed."
rustup component add rust-analyzer || warn "rust-analyzer (rustup) could not be installed."

### Fallback: rust-analyzer from the repos if the rustup call does not work.
if ! command -v rust-analyzer &>/dev/null; then
  warn "rust-analyzer not found in PATH. Installing from the repos ..."
  sudo pacman -S --noconfirm --needed rust-analyzer
fi

### ------------------------------------------------------------------
### 4. Check versions
### ------------------------------------------------------------------
info "Checking installations ..."
nvim --version | head -n 1
rustc --version
cargo --version
rust-analyzer --version 2>/dev/null || warn "rust-analyzer --version not available."

### ------------------------------------------------------------------
### 5. Create Neovim configuration directory
### ------------------------------------------------------------------
NVIM_CONFIG="$HOME/.config/nvim"
NVIM_LUA="$NVIM_CONFIG/lua"
NVIM_PLUGINS="$NVIM_LUA/plugins"

info "Creating Neovim configuration directories ..."
mkdir -p "$NVIM_CONFIG"
mkdir -p "$NVIM_LUA/config"
mkdir -p "$NVIM_PLUGINS"

### ------------------------------------------------------------------
### 6. Create init.lua
### ------------------------------------------------------------------
info "Creating $NVIM_CONFIG/init.lua ..."
cat > "$NVIM_CONFIG/init.lua" <<'EOF'
require("config.lazy")
EOF

### ------------------------------------------------------------------
### 7. Plugin init (empty so that lazy.nvim does not throw errors)
### ------------------------------------------------------------------
info "Creating $NVIM_PLUGINS/init.lua ..."
cat > "$NVIM_PLUGINS/init.lua" <<'EOF'
return {}
EOF

### ------------------------------------------------------------------
### 8. Create lazy.lua bootstrap file
### ------------------------------------------------------------------
info "Creating $NVIM_LUA/config/lazy.lua ..."
cat > "$NVIM_LUA/config/lazy.lua" <<'EOF'
-- ~/.config/nvim/lua/config/lazy.lua

-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
  local lazyrepo = "https://github.com/folke/lazy.nvim.git"
  local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
  if vim.v.shell_error ~= 0 then
    vim.api.nvim_echo({
      { "Failed to clone lazy.nvim:\n", "ErrorMsg" },
      { out, "WarningMsg" },
      { "\nPress any key to exit..." },
    }, true, {})
    vim.fn.getchar()
    os.exit(1)
  end
end
vim.opt.rtp:prepend(lazypath)

-- Leader keys must be set before lazy.nvim
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- lazy.nvim setup
require("lazy").setup({
  spec = {
    { import = "plugins" },
  },

  -- LuaRocks support with hererocks
  rocks = {
    enabled = true,
    hererocks = true,
  },

  -- Colorscheme when installing
  install = { colorscheme = { "habamax" } },

  -- Automatic update check
  checker = { enabled = true },

  -- Performance
  performance = {
    rtp = {
      disabled_plugins = {
        "gzip",
        "matchit",
        "matchparen",
        "netrwPlugin",
        "tarPlugin",
        "tohtml",
        "tutor",
        "zipPlugin",
      },
    },
  },
})
EOF

### ------------------------------------------------------------------
### 9. rustaceanvim plugin configuration
### ------------------------------------------------------------------
info "Creating $NVIM_PLUGINS/rust.lua ..."
cat > "$NVIM_PLUGINS/rust.lua" <<'EOF'
-- ~/.config/nvim/lua/plugins/rust.lua
return {
  {
    "mrcjkb/rustaceanvim",
    version = "^9", -- Version 9 for current Neovim versions
    lazy = false,   -- Important: Loads the plugin immediately
  },
}
EOF

### ------------------------------------------------------------------
### 10. blink.cmp plugin configuration
### ------------------------------------------------------------------
info "Creating $NVIM_PLUGINS/completion.lua ..."
cat > "$NVIM_PLUGINS/completion.lua" <<'EOF'
-- ~/.config/nvim/lua/plugins/completion.lua
return {
  {
    'saghen/blink.cmp',
    -- Optional: Provides snippets for snippet completion
    dependencies = { 'rafamadriz/friendly-snippets' },

    -- Uses a release tag to download prebuilt binaries.
    -- This is the recommended way for the best performance.
    version = '1.*',

    ---@module 'blink.cmp'
    ---@type blink.cmp.Config
    opts = {
      -- 'default' uses key bindings similar to those of the built-in
      -- completion (e.g.,  to accept)
      keymap = { preset = 'default' },

      appearance = {
        -- 'mono' for 'Nerd Font Mono' to align icons correctly
        nerd_font_variant = 'mono'
      },

      -- By default, suggestions from LSP, file paths,
      -- snippets, and the current buffer are used.
      sources = {
        default = { 'lsp', 'path', 'snippets', 'buffer' },
      },

      -- Uses the Rust implementation for the fuzzy matcher (faster),
      -- but automatically falls back to the Lua implementation.
      fuzzy = { implementation = "prefer_rust_with_warning" }
    },
    opts_extend = { "sources.default" }
  }
}
EOF

### ------------------------------------------------------------------
### 11. Install plugins headless (lazy.nvim sync)
### ------------------------------------------------------------------
info "Installing plugins via lazy.nvim (headless) ..."
if nvim --headless "+Lazy! sync" +qa 2>/dev/null; then
  info "Plugins successfully synchronized."
else
  warn "Headless sync failed. Please start Neovim manually and run ':Lazy sync'."
fi

### ------------------------------------------------------------------
### 12. Done
### ------------------------------------------------------------------
info "Done! Summary:"
echo "  - Neovim version:       $(nvim --version | head -n 1)"
echo "  - Rust version:         $(rustc --version)"
echo "  - Cargo version:        $(cargo --version)"
echo "  - rust-analyzer:        $(rust-analyzer --version 2>/dev/null || echo 'not available')"
echo "  - Neovim config:        $NVIM_CONFIG"
echo
info "Start Neovim with 'nvim' and check with ':Lazy' whether all plugins are installed."
info "Open a .rs file to test rustaceanvim and rust-analyzer."

Usage

  1. Save the file as e.g., install_neovim_rust.sh.

  2. Make it executable:

    chmod +x install_neovim_rust.sh
    
  3. Start it (not as root):

    ./install_neovim_rust.sh
    

What the Script Does

Step Action
1 System update via pacman -Syu
2 Installation of neovim, lua51, git, base-devel, rustup
3 Activation of the Rust stable toolchain
4 Adding rustfmt, clippy, rust-analyzer
5 Fallback: rust-analyzer from the repos if the rustup version is not in PATH
6 Creating the directories ~/.config/nvim/lua/{config,plugins}
7 Creating init.lua with require("config.lazy")
8 Creating the empty plugins/init.lua
9 Creating config/lazy.lua (bootstrap + setup)
10 Creating plugins/rust.lua (rustaceanvim)
11 Creating plugins/completion.lua (blink.cmp, preset default)
12 Headless installation of the plugins via nvim --headless "+Lazy! sync" +qa

Notes

  • The script deliberately uses the default preset for blink.cmp. The custom shortcuts (e.g., super-tab or enter) are not included – you can adjust them later in ~/.config/nvim/lua/plugins/completion.lua.
  • rust-analyzer is first installed via rustup. If that fails or the command does not end up in PATH, the package from the Manjaro repos is automatically installed afterwards.
  • The plugin installation runs headless. If that fails, you can simply start Neovim normally and run :Lazy sync.

Installation NeoVim in Manjaro

Hinweis: Wenn du es wesentlich einfacher haben willst, aber dennoch das Vim oder Neovim Feeling nicht missen willst, dann kannst du auch diesen Rust Editor benutzen: Zed - Your last next editor - https://zed.dev/

Manjaros offizielle Repositories bieten in der Regel eine aktuelle Version von Neovim.

Mit pacman

sudo pacman -Syu
sudo pacman -S neovim lua51

Nach der Installation kannst du die Version mit folgendem Befehl prüfen:

nvim --version

Installation Rust

Was ist Rustup - Toolchain

Vorteil von Rustup

Rustup ist der offizielle Toolchain-Manager für Rust. Ohne Rustup installierst du Rust einmalig über die Paketverwaltung deiner Distribution und bist an diese eine Version gebunden. Rustup bietet darüber hinaus folgende Vorteile:

Mehrere Toolchains parallel

Du kannst gleichzeitig die stable, beta und nightly Version von Rust installieren und zwischen ihnen wechseln:

rustup install nightly
rustup install beta
rustup default stable
rustup override set nightly   # nur für aktuelles Projekt

Das ist besonders nützlich, da einige Crates (Programme oder Bibliotheken) z. B. für experimentelle Features nur auf nightly laufen.

Projektbezogene Versionen

Mit einer rust-toolchain.toml Datei im Projektordner legst du fest, welche Rust-Version das Projekt verwendet:

[toolchain]
channel = "1.75.0"
components = ["rustfmt", "clippy"]

Sobald du in den Ordner wechselst, aktiviert Rustup automatisch die passende Version. Das stellt sicher, dass alle Teammitglieder und CI-Systeme dieselbe Version verwenden.

Pinning auf exakte Versionen

Du kannst gezielt eine bestimmte Rust-Version installieren, um reproduzierbare Builds zu gewährleisten:

rustup install 1.75.0
rustup default 1.75.0

Komponenten nachinstallieren

Zusätzliche Werkzeuge wie rustfmt, clippy, rust-analyzer oder rust-src lassen sich jederzeit ergänzen oder entfernen:

rustup component add clippy
rustup component remove rust-docs

Target-Unterstützung

Für Cross-Compilation kannst du Ziele für andere Plattformen hinzufügen:

rustup target add aarch64-unknown-linux-gnu
rustup target add x86_64-pc-windows-gnu

Updates

Rustup aktualisiert die Toolchains mit einem Befehl:

rustup update

Im Gegensatz dazu hängst du bei der Installation über pacman von den Update-Zyklen der Distribution ab, die oft Monate hinterherhinken.

Vorteil einer Toolchain

Eine Toolchain ist das eigentliche Rust-Komplettpaket und besteht aus mehreren Komponenten:

  • rustc: der Compiler
  • cargo: der Build-Manager und Package-Manager
  • rust-std: die Standardbibliothek
  • rust-docs: die lokale Dokumentation
  • optionale Komponenten wie rustfmt und clippy

Warum das wichtig ist

Der Vorteil liegt darin, dass alle Komponenten einer Toolchain aufeinander abgestimmt sind. Der Compiler, die Standardbibliothek und die Werkzeuge stammen aus derselben Version. Das vermeidet Versionskonflikte, wie sie bei manuell zusammengestellten Installationen auftreten können.

Kanal statt Version

Eine Toolchain wird über einen Kanal referenziert (stable, beta, nightly) oder über eine exakte Version (1.75.0). Dadurch kannst du entweder immer die neuesten stabilen Features nutzen oder auf eine feste Version setzen, wenn dein Projekt das erfordert.

Vergleich: Rustup vs. Paketmanager

Aspekt Rustup pacman
Version Aktuell Oft veraltet
Mehrere Versionen Ja Nein
Kanalwechsel Ja Nein
Projektbezogene Versionen Ja Nein
Komponenten nachinstallieren Ja Teilweise
Cross-Compilation-Targets Ja Eingeschränkt
Update-Zyklus Unabhängig Abhängig von Distribution

Wann reicht der Paketmanager?

Wenn du Rust nur gelegentlich nutzt, keine projektbezogenen Versionen brauchst und mit der Version deiner Distribution zufrieden bist, ist die Installation über pacman ausreichend. Für die professionelle Entwicklung ist Rustup jedoch die klar bessere Wahl.

Rustup installieren

Schritt 1: base-devel & Rustup aus dem Repository installieren

sudo pacman -S base-devel rustup

Schritt 2: Toolchain aktivieren

rustup default stable

Wichtig: Das rustup-Paket aus den Arch-Repositories installiert standardmäßig keine Toolchain. Die Binaries (rustc, cargo) sind symbolische Links auf rustup, daher muss die Toolchain manuell aktiviert werden.

Schritt 3: Installation überprüfen

rustc --version
cargo --version

Entwicklungswerkzeuge

Nach der Installation von Rustup kannst du zusätzliche Komponenten hinzufügen:

rustup component add rustfmt         # Code-Formatierung
rustup component add clippy          # Statische Analyse
rustup component add rust-analyzer   # Language Server für IDEs

Rust Installation testen

cargo new hello_world
cd hello_world
cargo run

NeoVim 4 Rust

Um Rust in Neovim zu programmieren, brauchst du im Wesentlichen Rust selbst (erledigt) , den Language Server rust-analyzer und eine Neovim-LSP-Konfiguration.

LSP - Language Server rust-analyzer

  1. rust-analyzer (der LSP-Server): Das ist das Herzstück für Code-Vervollständigung, Fehlerprüfung und Navigation. Der beste Weg ist, ihn über rustup als Komponente hinzuzufügen .
  2. rustup component add rust-analyzer
    

Weil es hier und da Probleme mit dem Aufruf von rust-analyzer gibt, sollte noch folgende Anpassung gemacht werden.

Sollte der Aufruf nicht funktionieren

rust-analyzer --version

installiere explizit und danach rufe den oberen Befehle nochmal auf

sudo pacman -S rust-analyzer

Das Ganze hat mit einem kaputten Symlink/Proxy zu tun. Eventuell später mehr dazu. Es soll jetzt erstmal funktionieren!

Neovim Konfiguration

# Übersicht der Verzeichnisse

~/.config/nvim/
├── init.lua                          # Einstiegspunkt, lädt config.lazy
└── lua/
    ├── config/
    │   └── lazy.lua                  # Bootstrap & Setup für lazy.nvim
    └── plugins/
        ├── init.lua                  # Platzhalter, verhindert Fehler
        ├── rust.lua                  # rustaceanvim
        └── completion.lua            # blink.cmp
  1. Lege das Konfigurationsverzeichnis an
mkdir -p ~/.config/nvim
  1. Installiere Plugin Manager lazy.vim

Bevor du beginnst, stelle sicher, dass auf deinem System Neovim (>= 0.8.0) und Git (>= 2.19.0) installiert sind.

nvim --version | head -n 1 && git --version

Falls Git noch nicht installiert ist:

sudo pacman -S git
  1. Konfigurationsdateien anlegen

Erstelle zuerst die benötigten Ordner und Dateien in deinem Neovim-Konfigurationsverzeichnis.

mkdir -p ~/.config/nvim/lua/config && mkdir -p ~/.config/nvim/lua/plugins
  1. init.lua erstellen oder anpassen

Erstelle die Datei ~/.config/nvim/init.lua (falls sie noch nicht existiert) und füge diese Zeile ein:

nvim ~/.config/nvim/init.lua
require("config.lazy")
  1. Bootstrap-Datei für lazy.nvim erstellen

Damit lazy.nvim keine Fehlermeldung zurück gibt, muss in ~/.config/nvim/lua/plugins/ eine leere init.lua angelegt werden.

echo "return {}" > ~/.config/nvim/lua/plugins/init.lua
  1. Erstelle die Datei ~/.config/nvim/lua/config/lazy.lua
nvim ~/.config/nvim/lua/config/lazy.lua

mit folgendem Inhalt. Dieser Code in lazy.lua lädt das Plugin lazy.nvim automatisch herunter, falls es noch nicht vorhanden ist:

-- ~/.config/nvim/lua/config/lazy.lua

-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
  local lazyrepo = "https://github.com/folke/lazy.nvim.git"
  local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
  if vim.v.shell_error ~= 0 then
    vim.api.nvim_echo({
      { "Failed to clone lazy.nvim:\n", "ErrorMsg" },
      { out, "WarningMsg" },
      { "\nPress any key to exit..." },
    }, true, {})
    vim.fn.getchar()
    os.exit(1)
  end
end
vim.opt.rtp:prepend(lazypath)

-- Leader-Tasten müssen vor lazy.nvim gesetzt werden
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- lazy.nvim Setup
require("lazy").setup({
  spec = {
    { import = "plugins" },
  },

  -- LuaRocks-Unterstützung mit hererocks
  rocks = {
    enabled = true,
    hererocks = true,
  },

  -- Farbschema beim Installieren
  install = { colorscheme = { "habamax" } },

  -- Automatische Update-Prüfung
  checker = { enabled = true },

  -- Performance
  performance = {
    rtp = {
      disabled_plugins = {
        "gzip",
        "matchit",
        "matchparen",
        "netrwPlugin",
        "tarPlugin",
        "tohtml",
        "tutor",
        "zipPlugin",
      },
    },
  },
})
  1. Installation prüfen

Nachdem du die Konfiguration gespeichert hast, starte Neovim neu. Das Plugin lazy.nvim sollte sich nun automatisch installieren.

lazy.nvim öffnen: Gib den Befehl :Lazy ein, um die grafische Oberfläche zu öffnen.

Sobald lazy.nvim läuft, kannst du mit dem nächsten Schritt fortfahren: der Einrichtung von rustaceanvim in deiner Plugin-Konfiguration.

  1. Plugin rustaceanvim installieren

Für die Konfiguration gibt es zwei gängige Wege. Der einfachste und modernste Weg ist ein spezielles Plugin für Rust.

Dieses Plugin ist eine Art “Rundum-sorglos-Paket” für Rust in Neovim. Es verbindet sich automatisch mit rust-analyzer und bringt viele nützliche Funktionen mit, ohne dass du den LSP-Server manuell konfigurieren musst .

Der rustaceanvim-Eintrag kommt nicht in die lazy.lua, sondern in eine separate Datei im plugins-Ordner. Die lazy.lua ist nur für den Bootstrap und die globale Konfiguration von lazy.nvim zuständig. Deine eigentlichen Plugins gehören in ~/.config/nvim/lua/plugins/.

Erstelle eine neue Datei:

nvim ~/.config/nvim/lua/plugins/rust.lua

Die Datei muss eine Tabelle zurückgeben. Der rustaceanvim-Eintrag steht in dieser Tabelle:

-- ~/.config/nvim/lua/plugins/rust.lua
return {
  {
    "mrcjkb/rustaceanvim",
    version = "^9", -- Version 9 für aktuelle Neovim-Versionen
    lazy = false,   -- Wichtig: Lädt das Plugin sofort
  },
}
  1. Speichere die Datei.
  2. Starte Neovim neu.
  3. gib :Lazy ein und eventuell U zum Updaten
  4. lazy.nvim erkennt die neue Datei und installiert rustaceanvim automatisch.
  5. Öffne eine .rs-Datei. rustaceanvim startet dann rust-analyzer.

Plugin blink.cmp - autocompletion

Installation

Erstelle folgende Datei :

nvim ~/.config/nvim/lua/plugins/completion.lua

Mit folgendem Inhalt

return {
  {
    'saghen/blink.cmp',
    -- Optional: Stellt Snippets für die Snippet-Vervollständigung bereit
    dependencies = { 'rafamadriz/friendly-snippets' },

    -- Nutzt einen Release-Tag, um vorgebaute Binärdateien herunterzuladen.
    -- Das ist der empfohlene Weg für die beste Performance.
    version = '1.*',

    ---@module 'blink.cmp'
    ---@type blink.cmp.Config
    opts = {
      -- 'default' verwendet Tastenkürzel, die denen der eingebauten
      -- Vervollständigung ähneln (z.B.  zum Akzeptieren)
      keymap = { preset = 'default' },

      appearance = {
        -- 'mono' für 'Nerd Font Mono', um Icons korrekt auszurichten
        nerd_font_variant = 'mono'
      },

      -- Standardmäßig werden Vorschläge von LSP, Dateipfaden,
      -- Snippets und dem aktuellen Buffer verwendet.
      sources = {
        default = { 'lsp', 'path', 'snippets', 'buffer' },
      },

      -- Nutzt die Rust-Implementierung für den Fuzzy-Matcher (schneller),
      -- fällt aber automatisch auf die Lua-Implementierung zurück.
      fuzzy = { implementation = "prefer_rust_with_warning" }
    },
    opts_extend = { "sources.default" }
  }
}

Shortcuts

blink.cmp verwendet standardmäßig das default-Preset, das sich an der eingebauten Neovim-Vervollständigung orientiert . Die wichtigsten Tastenkürzel im Insert-Modus sind:

  • : Menü öffnen oder Dokumentation umschalten .
  • : Ausgewählten Vorschlag akzeptieren .
  • : Menü schließen (und Vorschau rückgängig machen) .
  • / (oder Pfeil runter/hoch): Nächsten oder vorherigen Eintrag auswählen .
  • : Signatur-Hilfe umschalten (sofern aktiviert) .
  • / : In der Dokumentationsvorschau scrollen .
  • / : Zwischen Snippet-Platzhaltern springen .

Möchtest du ein anderes Verhalten, kannst du in deiner blink.cmp-Konfiguration einfach ein anderes Preset wählen, z. B. keymap = { preset = 'super-tab' } für Tab-zum-Akzeptieren .

Neben den von mir genannten Shortcuts gibt es bei blink.cmp noch einige weitere, die je nach Konfiguration und Kontext (z.B. in der Kommandozeile) verfügbar sind.

Presets

Presets sind bei blink.cmp vorgefertigte Tastaturbelegungen, die festlegen, wie du das Vervollständigungsmenü bedienst. Sie sind sozusagen „Pakete“ von Tastenkürzeln für bestimmte Arbeitsweisen.

Die wichtigsten Presets im Überblick

Du wählst ein Preset über keymap = { preset = 'name' } in deiner Konfiguration .

  • default: Orientiert sich an der eingebauten Neovim-Vervollständigung. Du bestätigst einen Vorschlag mit (wie „Yes“) .
  • super-tab: Orientiert sich an VS Code. Die Tab-Taste akzeptiert den ausgewählten Vorschlag .
  • enter: Verwendet die Enter-Taste () zum Akzeptieren. Die Tab-Taste bleibt für die Navigation zwischen Snippet-Platzhaltern reserviert .
  • cmdline: Ist speziell für die Kommandozeile (:) optimiert. Die Tab-Taste zeigt das Menü, fügt den ersten Eintrag ein oder wählt den nächsten aus .

Gemeinsame Tasten in allen Presets

Unabhängig vom gewählten Preset sind einige Tasten immer gleich belegt, zum Beispiel :

  • : Menü öffnen oder Dokumentation umschalten.
  • / (oder Pfeiltasten): Nächsten oder vorherigen Eintrag auswählen.
  • : Menü schließen.

Die wichtigsten Presets im Vergleich

blink.cmp bringt verschiedene vordefinierte Tastaturbelegungen mit, die sogenannten Presets. Deine aktuell genutzten Shortcuts (, , etc.) gehören zum default-Preset. Andere Presets ändern die Belegung grundlegend:

Funktion default super-tab enter cmdline
Akzeptieren (smart) (Enter)
Nächstes , , , ,
Vorheriges , , , ,
Snippet vorwärts (smart) —

Wenn du also z.B. möchtest, dass Tab den Vorschlag akzeptiert (wie in VS Code), änderst du keymap = { preset = 'super-tab' } in deiner Konfiguration .

Weitere nützliche Standard-Shortcuts

Unabhängig vom Preset gibt es einige Befehle, die in den meisten Konfigurationen zu finden sind:

  • : Schaltet die Signatur-Hilfe um (sofern aktiviert) .
  • / : Scrollt in der Dokumentationsvorschau nach oben/unten .
  • : Zeigt das Vervollständigungsmenü oder die Dokumentation an .
  • : Schließt das Menü (und macht eine automatische Vorschau rückgängig) .

Shortcuts in der Kommandozeile (cmdline)

Wenn du in der Neovim-Kommandozeile (:) tippst, gelten oft andere Regeln. Das cmdline-Preset ist speziell dafür optimiert :

  • : Zeigt das Menü, fügt das erste Element ein oder wählt das nächste aus.
  • : Wählt das vorherige Element aus.
  • / : Navigiert wie in der normalen Vervollständigung durch die Vorschläge.

Eigene Shortcuts definieren

Du kannst die Belegung jederzeit anpassen. Wenn du zum Beispiel (Enter) zum Akzeptieren nutzen möchtest, änderst du in deiner blink.cmp-Konfiguration den Eintrag keymap:

keymap = { preset = 'enter' },

Oder du definierst komplett eigene Kombinationen, indem du ein preset = 'none' setzt und die Tasten selbst belegst .

Beispiel:

-- ~/.config/nvim/lua/plugins/completion.lua
return {
  {
    'saghen/blink.cmp',
    dependencies = { 'rafamadriz/friendly-snippets' },
    version = '1.*',
    opts = {
      keymap = { preset = 'enter' }, -- Hier wird das Preset gewählt
      -- ... restliche Optionen wie appearance, sources, fuzzy
    },
  },
}

Installation Script für Neovim, Rust und Plugins unter Manjaro

Für Ungeduldige mit Risikobereitschaft: Hier ist ein vollständiges Bash-Skript, das alle Schritte aus deinem Leitfaden automatisiert. Es installiert Neovim, Rust (via Rustup), die Rust-Komponenten und richtet die komplette Neovim-Konfiguration mit lazy.nvim, rustaceanvim und blink.cmp ein.

#!/usr/bin/env bash
### ### install_neovim_rust.sh
### Installiert Neovim, Rust (Rustup) und richtet die Neovim-Konfiguration
### mit lazy.nvim, rustaceanvim und blink.cmp unter Manjaro ein.
### ### Autor: hoergen (Vorlage), automatisiert
### Datum: 2026-09-22
### 
set -euo pipefail

### ------------------------------------------------------------------
### Farben für Ausgaben
### ------------------------------------------------------------------
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color

info()  { echo -e "${GREEN}[INFO]${NC}  $*"; }
warn()  { echo -e "${YELLOW}[WARN]${NC}  $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }

### ------------------------------------------------------------------
### 0. Voraussetzungen prüfen
### ------------------------------------------------------------------
if [[ $EUID -eq 0 ]]; then
  error "Bitte NICHT als root ausführen. Das Skript nutzt sudo wo nötig."
  exit 1
fi

if ! command -v pacman &>/dev/null; then
  error "pacman nicht gefunden. Dieses Skript ist für Manjaro/Arch gedacht."
  exit 1
fi

### ------------------------------------------------------------------
### 1. System aktualisieren und Neovim + Lua installieren
### ------------------------------------------------------------------
info "Aktualisiere Paketdatenbank und System ..."
sudo pacman -Syu --noconfirm

info "Installiere Neovim, Lua 5.1, git, base-devel und rustup ..."
sudo pacman -S --noconfirm --needed \
  neovim \
  lua51 \
  git \
  base-devel \
  rustup

### ------------------------------------------------------------------
### 2. Rust-Toolchain aktivieren
### ------------------------------------------------------------------
info "Aktiviere Rust stable Toolchain ..."
if ! rustup toolchain list | grep -q '^stable'; then
  rustup default stable
else
  info "stable Toolchain ist bereits aktiv."
fi

### ------------------------------------------------------------------
### 3. Rust-Komponenten installieren
### ------------------------------------------------------------------
info "Installiere Rust-Komponenten: rustfmt, clippy, rust-analyzer ..."
rustup component add rustfmt   || warn "rustfmt konnte nicht installiert werden."
rustup component add clippy    || warn "clippy konnte nicht installiert werden."
rustup component add rust-analyzer || warn "rust-analyzer (rustup) konnte nicht installiert werden."

### Fallback: rust-analyzer aus den Repos, falls der rustup-Aufruf nicht klappt.
if ! command -v rust-analyzer &>/dev/null; then
  warn "rust-analyzer nicht im PATH gefunden. Installiere aus den Repos ..."
  sudo pacman -S --noconfirm --needed rust-analyzer
fi

### ------------------------------------------------------------------
### 4. Versionen prüfen
### ------------------------------------------------------------------
info "Überprüfe Installationen ..."
nvim --version | head -n 1
rustc --version
cargo --version
rust-analyzer --version 2>/dev/null || warn "rust-analyzer --version nicht verfügbar."

### ------------------------------------------------------------------
### 5. Neovim-Konfigurationsverzeichnis anlegen
### ------------------------------------------------------------------
NVIM_CONFIG="$HOME/.config/nvim"
NVIM_LUA="$NVIM_CONFIG/lua"
NVIM_PLUGINS="$NVIM_LUA/plugins"

info "Lege Neovim-Konfigurationsverzeichnisse an ..."
mkdir -p "$NVIM_CONFIG"
mkdir -p "$NVIM_LUA/config"
mkdir -p "$NVIM_PLUGINS"

### ------------------------------------------------------------------
### 6. init.lua erstellen
### ------------------------------------------------------------------
info "Erstelle $NVIM_CONFIG/init.lua ..."
cat > "$NVIM_CONFIG/init.lua" <<'EOF'
require("config.lazy")
EOF

### ------------------------------------------------------------------
### 7. Plugin-Init (leer, damit lazy.nvim keine Fehler wirft)
### ------------------------------------------------------------------
info "Erstelle $NVIM_PLUGINS/init.lua ..."
cat > "$NVIM_PLUGINS/init.lua" <<'EOF'
return {}
EOF

### ------------------------------------------------------------------
### 8. lazy.lua Bootstrap-Datei erstellen
### ------------------------------------------------------------------
info "Erstelle $NVIM_LUA/config/lazy.lua ..."
cat > "$NVIM_LUA/config/lazy.lua" <<'EOF'
-- ~/.config/nvim/lua/config/lazy.lua

-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
  local lazyrepo = "https://github.com/folke/lazy.nvim.git"
  local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
  if vim.v.shell_error ~= 0 then
    vim.api.nvim_echo({
      { "Failed to clone lazy.nvim:\n", "ErrorMsg" },
      { out, "WarningMsg" },
      { "\nPress any key to exit..." },
    }, true, {})
    vim.fn.getchar()
    os.exit(1)
  end
end
vim.opt.rtp:prepend(lazypath)

-- Leader-Tasten müssen vor lazy.nvim gesetzt werden
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- lazy.nvim Setup
require("lazy").setup({
  spec = {
    { import = "plugins" },
  },

  -- LuaRocks-Unterstützung mit hererocks
  rocks = {
    enabled = true,
    hererocks = true,
  },

  -- Farbschema beim Installieren
  install = { colorscheme = { "habamax" } },

  -- Automatische Update-Prüfung
  checker = { enabled = true },

  -- Performance
  performance = {
    rtp = {
      disabled_plugins = {
        "gzip",
        "matchit",
        "matchparen",
        "netrwPlugin",
        "tarPlugin",
        "tohtml",
        "tutor",
        "zipPlugin",
      },
    },
  },
})
EOF

### ------------------------------------------------------------------
### 9. rustaceanvim Plugin-Konfiguration
### ------------------------------------------------------------------
info "Erstelle $NVIM_PLUGINS/rust.lua ..."
cat > "$NVIM_PLUGINS/rust.lua" <<'EOF'
-- ~/.config/nvim/lua/plugins/rust.lua
return {
  {
    "mrcjkb/rustaceanvim",
    version = "^9", -- Version 9 für aktuelle Neovim-Versionen
    lazy = false,   -- Wichtig: Lädt das Plugin sofort
  },
}
EOF

### ------------------------------------------------------------------
### 10. blink.cmp Plugin-Konfiguration
### ------------------------------------------------------------------
info "Erstelle $NVIM_PLUGINS/completion.lua ..."
cat > "$NVIM_PLUGINS/completion.lua" <<'EOF'
-- ~/.config/nvim/lua/plugins/completion.lua
return {
  {
    'saghen/blink.cmp',
    -- Optional: Stellt Snippets für die Snippet-Vervollständigung bereit
    dependencies = { 'rafamadriz/friendly-snippets' },

    -- Nutzt einen Release-Tag, um vorgebaute Binärdateien herunterzuladen.
    -- Das ist der empfohlene Weg für die beste Performance.
    version = '1.*',

    ---@module 'blink.cmp'
    ---@type blink.cmp.Config
    opts = {
      -- 'default' verwendet Tastenkürzel, die denen der eingebauten
      -- Vervollständigung ähneln (z.B.  zum Akzeptieren)
      keymap = { preset = 'default' },

      appearance = {
        -- 'mono' für 'Nerd Font Mono', um Icons korrekt auszurichten
        nerd_font_variant = 'mono'
      },

      -- Standardmäßig werden Vorschläge von LSP, Dateipfaden,
      -- Snippets und dem aktuellen Buffer verwendet.
      sources = {
        default = { 'lsp', 'path', 'snippets', 'buffer' },
      },

      -- Nutzt die Rust-Implementierung für den Fuzzy-Matcher (schneller),
      -- fällt aber automatisch auf die Lua-Implementierung zurück.
      fuzzy = { implementation = "prefer_rust_with_warning" }
    },
    opts_extend = { "sources.default" }
  }
}
EOF

### ------------------------------------------------------------------
### 11. Plugins headless installieren (lazy.nvim sync)
### ------------------------------------------------------------------
info "Installiere Plugins über lazy.nvim (headless) ..."
if nvim --headless "+Lazy! sync" +qa 2>/dev/null; then
  info "Plugins erfolgreich synchronisiert."
else
  warn "Headless-Sync fehlgeschlagen. Bitte Neovim manuell starten und ':Lazy sync' ausführen."
fi

### ------------------------------------------------------------------
### 12. Fertig
### ------------------------------------------------------------------
info "Fertig! Zusammenfassung:"
echo "  - Neovim-Version:       $(nvim --version | head -n 1)"
echo "  - Rust-Version:         $(rustc --version)"
echo "  - Cargo-Version:        $(cargo --version)"
echo "  - rust-analyzer:        $(rust-analyzer --version 2>/dev/null || echo 'nicht verfügbar')"
echo "  - Neovim-Konfig:        $NVIM_CONFIG"
echo
info "Starte Neovim mit 'nvim' und prüfe mit ':Lazy', ob alle Plugins installiert sind."
info "Öffne eine .rs-Datei, um rustaceanvim und rust-analyzer zu testen."

Verwendung

  1. Datei speichern als z. B. install_neovim_rust.sh.

  2. Ausführbar machen:

    chmod +x install_neovim_rust.sh
    
  3. Starten (nicht als root):

    ./install_neovim_rust.sh
    

Was das Skript macht

Schritt Aktion
1 System-Update via pacman -Syu
2 Installation von neovim, lua51, git, base-devel, rustup
3 Aktivierung der Rust-stable-Toolchain
4 Hinzufügen von rustfmt, clippy, rust-analyzer
5 Fallback: rust-analyzer aus den Repos, falls rustup-Version nicht im PATH
6 Anlegen der Verzeichnisse ~/.config/nvim/lua/{config,plugins}
7 Erstellen von init.lua mit require("config.lazy")
8 Erstellen der leeren plugins/init.lua
9 Erstellen der config/lazy.lua (Bootstrap + Setup)
10 Erstellen der plugins/rust.lua (rustaceanvim)
11 Erstellen der plugins/completion.lua (blink.cmp, Preset default)
12 Headless-Installation der Plugins via nvim --headless "+Lazy! sync" +qa

Hinweise

  • Das Skript verwendet bewusst das default-Preset für blink.cmp. Die Custom-Shortcuts (z. B. super-tab oder enter) sind nicht enthalten – du kannst sie später in ~/.config/nvim/lua/plugins/completion.lua anpassen.
  • rust-analyzer wird zuerst über rustup installiert. Falls das fehlschlägt oder der Befehl nicht im PATH landet, wird automatisch das Paket aus den Manjaro-Repos nachinstalliert.
  • Die Plugin-Installation läuft headless. Sollte das fehlschlagen, kannst du Neovim einfach normal starten und :Lazy sync ausführen.

I really like the Vim editor and I also want to use it for programming. However, doing so with the original Vim is quite complicated. So I took a closer look at NeoVim. A more modern fork of Vim. I just looked up the fundamental differences to see which one I need to use now, or whether I can simply keep using “both.” Spoiler alert: I can simply keep using both.

And I will try to build a development environment in Rust that is easy to follow, and of course I will also publish the how-to for it here.

Feature Vim Neovim
Project philosophy Stability, backward compatibility; shaped by a single lead developer (Bram Moolenaar, † 2023) Community-driven, rapid iteration, focus on modernization and extensibility
Configuration language Vimscript (VimL), plus Vim9script for new plugins Lua (native), Vimscript is still supported
LSP support (code completion, error checking) Not built in; requires plugins such as coc.nvim or vim-lsp Natively built in (vim.lsp)
Asynchronous processing Historically limited; plugins can block Native via libuv; background processes without blocking
Plugin language Vimscript, Python, Ruby (often with compatibility problems) Lua, Vimscript; remote plugins via msgpack-RPC in any language
Availability on servers Standard on practically every Linux/Unix system; essential for SSH workflows Rarely preinstalled; often has to be installed first
Configuration directory ~/.vim/ (historical, hardcoded) ~/.config/nvim/ (follows the XDG standard)

The Most Important Practical Consequence

The choice depends heavily on my work environment:

  • If I work a lot on remote servers (SSH): Vim is the safe bet, because it is preinstalled almost everywhere
  • If I want a modern, IDE-like experience locally with plugins and LSP: Neovim offers the more powerful and more modern foundation

Parallel Installation - Yes!

Easily doable. Vim and Neovim are completely independent programs with different binary names (vim vs. nvim), separate configuration directories, and their own plugin folders. They do not interfere with each other.

Separate Configurations

Vim Neovim
Configuration file ~/.vimrc ~/.config/nvim/init.vim or init.lua
Plugin directory ~/.vim/ ~/.local/share/nvim/
Plugin manager (examples) vim-plug, Vundle lazy.nvim, packer.nvim

Since both use completely separate paths, I can have different plugins, themes, and settings in each without any conflicts arising.

Possible Overlap

There is one small detail: Some distributions offer a package named vim that is actually a symlink to neovim (or vice versa). However, this is rare and only the case on systems deliberately configured that way. Normally the following applies:

  • vim starts Vim
  • nvim starts Neovim

I can check this with which vim and which nvim.

Practical Tip: Default Editor

If I use both in parallel, it is worth setting Neovim as the default editor (e.g., via $EDITOR or alternatives) and keeping Vim as a fallback for servers/SSH. That way I have the modern environment locally and still always have a working editor on servers.

Super Flexible as an Alias

If you frequently switch between the two, you can create aliases in your ~/.bashrc. For example, on Debian/Ubuntu:

alias vim='nvim'
alias vim-old='/usr/bin/vim.basic'

On Arch and Manjaro, the alias for the original Vim would simply be alias vim-old='vim', because there vim still points to the original Vim.

Permanent - Debian & Ubuntu

For Ubuntu and Debian, you use update-alternatives to register Neovim as the system-wide default.

#### Register Neovim as an alternative for vi, vim, view, and vimdiff
sudo update-alternatives --install /usr/bin/vi vi /usr/bin/nvim 60
sudo update-alternatives --install /usr/bin/vim vim /usr/bin/nvim 60
sudo update-alternatives --install /usr/bin/view view /usr/bin/nvim 60
sudo update-alternatives --install /usr/bin/vimdiff vimdiff /usr/bin/nvim 60

The 60 at the end is the priority. Since Neovim is usually installed with a lower priority than Vim, this command ensures that vim and vi point to nvim.

To check whether it worked:

which vim
#### should output /usr/bin/nvim

If you want to change the assignment again later, you can run sudo update-alternatives --config vim and select interactively.

On Ubuntu, there is also the command sudo select-editor, which shows you a list of available editors and lets you choose. This is the easiest way if you do not want to manually edit configuration files.

Permanent - Arch & Manjaro

For Manjaro and Arch Linux, you instead set the environment variables. This is also the cleaner way, because it works independently of the distribution.

#### System-wide for all users
sudo tee -a /etc/environment <EDITOR=nvim
VISUAL=nvim
EOF

The variables become active for all users at the next login. One advantage: Programs that access $EDITOR or $VISUAL then automatically use Neovim.

As the sudo Editor

A note for sudo: If you run sudo visudo or something similar, the environment variables from /etc/environment are not adopted by default, because sudo sanitizes the environment for security reasons. In that case, you can use sudo -E visudo or add the variable in the sudoers file with Defaults env_keep += "EDITOR VISUAL".

sudo -E visudo - temporary

The -E parameter (for --preserve-env) causes sudo to completely preserve the environment for that one command. So you would always have to type sudo -E visudo.

That is impractical if you need it often. The entry in /etc/sudoers is the permanent solution so that you can simply use sudo visudo and still get Neovim.

sudoers - permanent

You enter the line Defaults env_keep += "EDITOR VISUAL" in the file /etc/sudoers. You never do this directly, but always via the command sudo visudo. The tool checks the syntax before the change is saved, thus preventing you from wrecking your system.

When you run sudo visudo, /etc/sudoers opens in an editor. You look for the line that begins with Defaults and add your line below it or at the end of the Defaults section.

An excerpt from the file then looks like this:

## Override built-in defaults
Defaults    env_reset
Defaults    env_keep += "EDITOR VISUAL"

The line Defaults env_reset is usually already present. It ensures that sudo uses a clean environment. With Defaults env_keep += "EDITOR VISUAL" you tell sudo that it should keep the variables EDITOR and VISUAL from your environment.

Why This Is Necessary

By default, sudo sanitizes the environment. So if you have set EDITOR=nvim and then run sudo visudo, sudo no longer sees this variable. It then falls back to the default editor, often vi or nano.

With the env_keep entry, your EDITOR variable survives the sudo call, and visudo opens with Neovim (or whatever you have set).

Sources

Ich mag den Editor Vim sehr und ich möchte ihn auch zum Programmieren benutzen. Allerdings ist das mit dem originalen Vim ziemlich kompliziert. Daher habe ich mir mal NeoVim häher angeschaut. Ein modernerer Fork von Vim. Einfach mal die Grundsätzlichen Unterschiede rausgesucht, um zu sehen, welchen ich nun nehmen muss, oder ob ich einfach “beide” weiter benutzen kann. Spoiler Alert: Ich kann einfach beide weiter benutzen.

Und ich werde versuchen mir ein Developement Environment in Rust aufzubauen, das einfach nachvollziehbar ist und das How-To dazu werde ich natürlich auch hier veröffentlichen.

Merkmal Vim Neovim
Projektphilosophie Stabilität, Abwärtskompatibilität; von einem Hauptentwickler (Bram Moolenaar, † 2023) geprägt Community-getrieben, schnelle Iteration, Fokus auf Modernisierung und Erweiterbarkeit
Konfigurationssprache Vimscript (VimL), plus Vim9script für neue Plugins Lua (nativ), Vimscript wird weiterhin unterstützt
LSP-Support (Code-Completion, Fehlerprüfung) Nicht eingebaut; erfordert Plugins wie coc.nvim oder vim-lsp Nativ eingebaut (vim.lsp)
Asynchrone Verarbeitung Historisch limitiert; Plugins können blockieren Nativ über libuv; Hintergrundprozesse ohne Blockierung
Plugin-Sprache Vimscript, Python, Ruby (oft mit Kompatibilitätsproblemen) Lua, Vimscript; Remote-Plugins über msgpack-RPC in beliebigen Sprachen
Verfügbarkeit auf Servern Standard auf praktisch jedem Linux/Unix-System; essenziell für SSH-Workflows Selten vorinstalliert; muss oft erst installiert werden
Konfigurationsverzeichnis ~/.vim/ (Historisch, hartcodiert) ~/.config/nvim/ (Folgt XDG-Standard)

Die wichtigste praktische Konsequenz

Die Wahl hängt stark von meinem Arbeitsumfeld ab:

  • Wenn ich viel auf Remote-Servern arbeite (SSH): Vim ist die sichere Bank, weil es fast überall vorinstalliert ist
  • Wenn ich lokal eine moderne, IDE-ähnliche Erfahrung mit Plugins und LSP will: Neovim bietet die leistungsfähigere und modernere Basis

Parallelinstallation - Ja!

Problemlos machbar. Vim und Neovim sind völlig eigenständige Programme mit unterschiedlichen Binärnamen (vim vs. nvim), getrennten Konfigurationsverzeichnissen und eigenen Plugin-Ordnern. Sie behindern sich gegenseitig nicht.

Getrennte Konfigurationen

Vim Neovim
Konfigurationsdatei ~/.vimrc ~/.config/nvim/init.vim oder init.lua
Plugin-Verzeichnis ~/.vim/ ~/.local/share/nvim/
Plugin-Manager (Beispiele) vim-plug, Vundle lazy.nvim, packer.nvim

Da beide komplett getrennte Pfade verwenden, kann ich in beiden unterschiedliche Plugins, Themes und Einstellungen haben, ohne dass es zu Konflikten kommt.

Mögliche Überschneidung

Es gibt ein kleines Detail: Manche Distributionen bieten ein Paket namens vim an, das eigentlich ein Symlink auf neovim ist (oder umgekehrt). Das ist aber selten und nur bei bewusst so konfigurierten Systemen der Fall. Normalerweise gilt:

  • vim startet Vim
  • nvim startet Neovim

Ich kann das mit which vim und which nvim prüfen.

Praktischer Tipp: Standard Editor

Wenn ich beide parallel nutze, lohnt es sich, Neovim als Standard-Editor zu setzen (z. B. über $EDITOR oder alternatives) und Vim als Fallback für Server/SSH zu behalten. So habe ich lokal die moderne Umgebung und auf Servern trotzdem immer einen funktionierenden Editor.

Superflexibel als Alias

Wenn du häufig zwischen beiden wechselst, kannst du dir Aliase in deiner ~/.bashrc anlegen. Zum Beispiel bei Debian/Ubuntu:

alias vim='nvim'
alias vim-old='/usr/bin/vim.basic'

Auf Arch und Manjaro wäre der Alias für den originalen Vim einfach alias vim-old='vim', weil vim dort noch auf den originalen Vim zeigt.

Permanent - Debian & Ubuntu

Für Ubuntu und Debian nutzt du update-alternatives, um Neovim systemweit als Standard zu registrieren.

#### Neovim als Alternative für vi, vim, view und vimdiff registrieren
sudo update-alternatives --install /usr/bin/vi vi /usr/bin/nvim 60
sudo update-alternatives --install /usr/bin/vim vim /usr/bin/nvim 60
sudo update-alternatives --install /usr/bin/view view /usr/bin/nvim 60
sudo update-alternatives --install /usr/bin/vimdiff vimdiff /usr/bin/nvim 60

Die 60 am Ende ist die Priorität. Da Neovim in der Regel mit einer niedrigeren Priorität installiert wird als Vim, sorgt dieser Befehl dafür, dass vim und vi auf nvim zeigen .

Um zu prüfen, ob es funktioniert hat:

which vim
#### sollte /usr/bin/nvim ausgeben

Falls du die Zuordnung später wieder ändern möchtest, kannst du sudo update-alternatives --config vim ausführen und interaktiv auswählen .

Auf Ubuntu gibt es zusätzlich den Befehl sudo select-editor, der dir eine Liste der verfügbaren Editoren anzeigt und dich auswählen lässt . Das ist der einfachste Weg, wenn du nicht manuell in Konfigurationsdateien eingreifen möchtest.

Permanent - Arch & Manjaro

Für Manjaro und Arch Linux setzt du stattdessen die Umgebungsvariablen. Das ist auch der sauberere Weg, weil es unabhängig von der Distribution funktioniert.

#### Systemweit für alle Benutzer
sudo tee -a /etc/environment <EDITOR=nvim
VISUAL=nvim
EOF

Die Variablen werden beim nächsten Login für alle Benutzer aktiv . Ein Vorteil: Programme, die auf $EDITOR oder $VISUAL zugreifen, nutzen dann automatisch Neovim.

Als sudo Editor

Ein Hinweis für sudo: Wenn du sudo visudo oder ähnliches ausführst, werden die Umgebungsvariablen aus /etc/environment standardmäßig nicht übernommen, weil sudo die Umgebung aus Sicherheitsgründen bereinigt . In dem Fall kannst du sudo -E visudo nutzen oder die Variable in der sudoers-Datei mit Defaults env_keep += "EDITOR VISUAL" ergänzen.

sudo -E visudo - temporär

Der -E-Parameter (für --preserve-env) sorgt dafür, dass sudo die Umgebung für diesen einen Befehl komplett beibehält. Du müsstest also immer sudo -E visudo tippen .

Das ist unpraktisch, wenn du es oft brauchst. Der Eintrag in /etc/sudoers ist die dauerhafte Lösung, damit du einfach sudo visudo nutzen kannst und trotzdem Neovim bekommst.

sudoers - permanent

Die Zeile Defaults env_keep += "EDITOR VISUAL" trägst du in die Datei /etc/sudoers ein. Das machst du niemals direkt, sondern immer über den Befehl sudo visudo. Das Tool prüft die Syntax, bevor die Änderung gespeichert wird, und verhindert so, dass du dir das System zerschießt.

Wenn du sudo visudo ausführst, öffnet sich die /etc/sudoers in einem Editor. Du suchst die Zeile, die mit Defaults beginnt, und fügst darunter oder am Ende der Defaults-Sektion deine Zeile hinzu.

Ein Ausschnitt aus der Datei sieht dann so aus:

## Override built-in defaults
Defaults    env_reset
Defaults    env_keep += "EDITOR VISUAL"

Die Zeile Defaults env_reset ist meist schon vorhanden. Sie sorgt dafür, dass sudo eine saubere Umgebung nutzt. Mit Defaults env_keep += "EDITOR VISUAL" sagst du sudo, dass es die Variablen EDITOR und VISUAL aus deiner Umgebung behalten soll .

Warum das nötig ist

Standardmäßig bereinigt sudo die Umgebung. Wenn du also EDITOR=nvim gesetzt hast und dann sudo visudo ausführst, sieht sudo diese Variable nicht mehr. Es fällt dann auf den Standard-Editor zurück, oft vi oder nano .

Mit dem env_keep-Eintrag überlebt deine EDITOR-Variable den sudo-Aufruf, und visudo öffnet sich mit Neovim (oder was auch immer du eingestellt hast).

Quellen

What Folds Actually Are

In Vim, folds are collapsible and expandable regions in a text document. You can use them to collapse entire paragraphs, functions, or code blocks so that only the first line remains visible. This makes navigating large files considerably easier.

The mnemonic for the command is simple. All fold commands begin with z, because the z looks like a folded piece of paper viewed from the side.

The Six Fold Methods

Vim offers six different methods for how folds are created. You set them with :set foldmethod=METHOD or enter them permanently in your ~/.vimrc.

  • manual - You create folds yourself with zf, for unstructured text
  • indent - Indentation determines the fold depth, ideal for program code
  • expr - Folds are defined by an expression, for log files or special filters
  • syntax - Syntax highlighting defines the folds, ideal for program code
  • diff - Unchanged text is folded
  • marker - Markers in the text such as {{{ and }}} define folds

For everyday use, indent for code and manual for everything else are the most practical methods.

foldlevel - Controlling the Fold Depth

The foldlevel option determines how many levels of folds are open or closed at the same time. It is the central control for the fold depth in the current window.

The Typical Values

  • foldlevel=0 - All folds are closed. You only see the outermost structure, for example chapter headings or top-level functions
  • foldlevel=1 - Only the top level is expanded, folds beneath it remain closed. Good for an overview of the main blocks
  • foldlevel=2 - Two levels are visible, deeper folds remain closed. Practical for nested code structures
  • foldlevel=99 - Practically everything is open. Only explicitly closed folds remain closed

foldlevel vs. foldlevelstart

An important difference: foldlevel applies to the current window and takes effect immediately. foldlevelstart determines the fold depth with which a file is opened.

So if you want a file to be completely expanded when opened, you set foldlevelstart=99 in your ~/.vimrc.

Important Commands

  • :set foldlevel? shows the current value
  • :setlocal foldlevel=1 sets the depth only for the current window
  • 2zM closes all folds up to level 2, leaving level 1 open

Saving Folds Permanently

An important point that many people do not know. When you close a file, manually created folds are lost. Vim does not remember the state automatically.

For this there are the commands :mkview and :loadview. With :mkview you save the current state including the folds. When you open the file again later, you load everything back with :loadview. You can save up to ten different views per file.

Storage Location of Folds

Vim saves the views in the directory defined in the viewdir option. The default value depends on your operating system:

  • Linux and Unix (including macOS): ~/.vim/view
  • Windows: $VIM/vimfiles/view

In this folder, Vim creates a separate view file for each file. The file name is generated from the path of the original file, with special characters replaced by equals signs (=).

Finding Out the Current Storage Location

You can display the path directly in Vim:

:set viewdir?

The output shows you where Vim currently searches or saves.

Changing the Storage Location

If you want to change the default path (for example, to keep your home directory tidy), you can adjust the option in your ~/.vimrc:

set viewdir=~/.vim/my-views

In newer Vim versions, the default value now also respects the XDG_CONFIG_HOME environment variable and then uses ~/.config/vim/view.

Deleting View Files

Since there is no built-in command for deleting, you have to remove the files manually. Simply navigate to the folder mentioned above and delete the corresponding files.

The Most Important Commands for Everyday Use

You should know the basic commands, then you will already get very far.

Opening and Closing

  • zo opens a fold under the cursor (open)
  • zc closes a fold under the cursor (close)
  • za toggles the fold, opens if closed, closes if open (alternate)
  • zR opens all folds in the document (Reset)
  • zM closes all folds in the document (Minimize)

Navigation Between Folds

Combination with the classic navigation commands:

  • zj jumps to the next fold
  • zk jumps to the previous fold
  • [z jumps to the beginning of the current fold
  • ]z jumps to the end of the current fold

Creating and Deleting

  • zf followed by a motion creates a fold, for example zfap for a whole paragraph (fold)
  • zd deletes the fold under the cursor, the text is preserved (delete)
  • zE deletes all folds in the window (Eliminate)

Practical Examples - Fold Methods

Vim offers six different methods for creating folds. Which one you choose depends on what you are editing and how much control you want.

manual - Manual Folding with zf

The most direct method. You mark a region and fold it with zf. This works visually, with motions, or with brackets.

Visually mark and fold

  1. Mark the region with v (character-wise), V (line-wise), or Ctrl-v (block-wise)

  2. Press zf

Vim creates a fold from it. The text is preserved, only hidden.

Folding with motions

  • zfap folds a whole paragraph (a paragraph)
  • zfG folds from the cursor to the end of the file (Go to end)
  • zfgg folds from the beginning of the file to the cursor (go all the way up)

Folding Between Brackets

The most elegant way for code. Place the cursor on an opening bracket such as { or [ and press zf%. Vim jumps to the matching closing bracket and folds everything in between.

An example in a JavaScript file:

function calculateSum(a, b) {
  const result = a + b;
  console.log(result);
  return result;
}

With the cursor on the opening { and zf%, it then looks like this:

+-- 4 lines: function calculateSum(a, b) {

indent - Folding by Indentation

The most practical method for code. Vim automatically creates folds based on the indentation depth.

:set foldmethod=indent

Each level of indentation becomes a fold level. When opening a file with foldlevel=1, all functions are collapsed and you see only the structure at a glance.

marker - Folding by Markers

You place special markers in the text that Vim recognizes as fold boundaries. The default markers are {{{ and }}}.

:set foldmethod=marker

An example:


<div class="container">
  <p>First linep>
  <p>Second linep>
div>

The text between the markers becomes a fold. The advantage: The fold is preserved when the file is opened again. The disadvantage: You are modifying the file itself.

syntax - Folding by Syntax

Vim uses the existing syntax rules to create folds. For most programming languages, these rules are already present.

:set foldmethod=syntax

For Ruby code, for example, all methods are automatically folded. You do not need to configure anything further.

expr - Folding by Expression

The most flexible method. You define an expression that decides for each line whether it is folded.

:set foldmethod=expr
:set foldexpr=getline(v:lnum)=~'ERROR'

This example folds all lines that do not contain the word ERROR. Ideal for log files, to leave only the errors visible.

diff - Folding by diff

When you compare two versions of a file, unchanged regions are automatically folded. This method is usually activated automatically when you use vimdiff or :diffthis.

Practical Application

For code files, I usually use foldmethod=indent in combination with foldlevel=1. Then, when opening a file, all functions are collapsed and I see only the structure at a glance. With za I then expand the function.

For longer text documents, foldmethod=manual is often better. Then I can decide for myself which paragraphs I collapse.

And if the folds ever become too much, zR or zn helps to open everything again.

Sources

Vim is available as open-source software.

Deleting Old Views - Vimscript

Here is a Vimscript that finds and deletes orphaned view files, along with instructions for integrating it.

The script uses the functions readdir() to list the view folder and delete() to delete the files. The reversal of the naming convention (=+ to /) is the core of the check.

The Reversal of the Naming Convention

This is the crucial point. Vim does not save view files under the original name, but encodes the complete path into the file name. Every slash (/) is replaced by the string =+.

An example. The original file /home/user/test.txt becomes a view file with the name =+home=+user=+test.txt in the view folder.

" Function to clean up orphaned view files
function! s:CleanOrphanedViews()
    " Determine the view directory (default: ~/.vim/view)
    let view_dir = expand(&viewdir)
    
    " Check whether the view directory exists
    if !isdirectory(view_dir)
        echohl WarningMsg
        echo "View directory not found: " . view_dir
        echohl None
        return
    endif

    let orphaned_count = 0
    
    " List all files in the view directory
    for view_file in readdir(view_dir)
        let view_path = view_dir . '/' . view_file
        
        " Only process files (no subfolders)
        if !filereadable(view_path)
            continue
        endif

        " Convert the file name back into a path
        " Vim replaces '/' with '=+' in the file name
        let original_path = substitute(view_file, '=+', '/', 'g')
        
        " Check whether the original file still exists
        " filereadable() is more robust than glob() with permission problems
        if !filereadable(original_path)
            " File no longer exists -> delete view
            if delete(view_path) == 0
                let orphaned_count += 1
                echom "Orphaned view deleted: " . view_file
            else
                echohl WarningMsg
                echom "Error deleting: " . view_file
                echohl None
            endif
        endif
    endfor

    " Output summary
    if orphaned_count > 0
        echom "Cleanup complete: " . orphaned_count . " orphaned view(s) deleted."
    else
        echom "No orphaned view files found."
    endif
endfunction

" Define command to call the function
command! CleanViews call s:CleanOrphanedViews()

How the Script Works

  1. Determine directory: The script reads the global option &viewdir, which by default points to ~/.vim/view (Unix) or $VIM/vimfiles/view (Windows).
  2. List files: With readdir(), all entries in the view folder are read.
  3. Reconstruct path: The crucial step. Vim encodes the original path in the view file name by replacing every slash (/) with the string =+. The substitute() function reverses this replacement.
  4. Existence check: filereadable() checks whether the original file reconstructed in this way still exists and is readable. This is more robust than a pure glob() check if permissions are involved.
  5. Deleting: If the original file is missing, the view file is removed with the built-in delete() function.

Integrating into the Vim Configuration

You have two options for using the script permanently.

Option 1: Insert directly into .vimrc

Copy the entire code block into your ~/.vimrc (or ~/.config/nvim/init.vim). After saving and restarting Vim, you can use the command :CleanViews.

Option 2: As a separate plugin (recommended for tidiness)

  1. Create a file named cleanviews.vim in the directory ~/.vim/plugin/.
  2. Insert the code there.
  3. Vim automatically loads files in ~/.vim/plugin/ at startup.

In both cases, the command :CleanViews is available. After running, it outputs a message stating how many orphaned view files were deleted.

Was Folds überhaupt sind

In Vim sind Folds zusammen- und ausklappbare Bereiche in einem Textdokument. Du kannst damit ganze Absätze, Funktionen oder Codeblöcke zusammenklappen, so dass nur die erste Zeile sichtbar bleibt. Das macht das Navigieren in umfangreichen Dateien deutlich einfacher.

Die Eselsbrücke für den Befehl ist einfach. Alle Fold-Kommandos beginnen mit z, weil das z aussieht wie ein gefaltetes Stück Papier von der Seite betrachtet.

Die sechs Fold-Methoden

Vim bietet sechs verschiedene Methoden, wie Faltungen erstellt werden. Du stellst sie mit :set foldmethod=METHODE ein oder trägst sie dauerhaft in deine ~/.vimrc ein.

  • manual - Du erstellst Faltungen selbst mit zf, für unstrukturierte Texte:
  • indent - Einrückung bestimmt die Faltungstiefe, ideal für Programmcode
  • expr - Faltungen werden durch einen Ausdruck definiert, für Log-Dateien oder spezielle Filter
  • syntax - Syntax-Highlighting definiert die Faltungen, ideal für Programmcode
  • diff - Unveränderter Text wird gefaltet
  • marker - Marker im Text wie {{{ und }}} definieren Faltungen

Für den Alltag sind indent für Code und manual für alles andere die praktischsten Methoden.

foldlevel - Die Faltungstiefe steuern

Die Option foldlevel legt fest, wie viele Ebenen von Faltungen gleichzeitig geöffnet oder geschlossen sind. Sie ist die zentrale Steuerung für die Faltungstiefe im aktuellen Fenster .

Die typischen Werte

  • foldlevel=0 - Alle Faltungen sind geschlossen. Du siehst nur die äußerste Struktur, zum Beispiel Kapitelüberschriften oder Top-Level-Funktionen
  • foldlevel=1 - Nur die oberste Ebene ist aufgeklappt, darunterliegende Faltungen bleiben geschlossen. Gut für einen Überblick über die Hauptblöcke
  • foldlevel=2 - Zwei Ebenen sind sichtbar, tiefere Faltungen bleiben zu. Praktisch für verschachtelte Code-Strukturen
  • foldlevel=99 - Praktisch alles ist geöffnet. Nur explizit geschlossene Faltungen bleiben zu

foldlevel vs. foldlevelstart

Ein wichtiger Unterschied: foldlevel gilt für das aktuelle Fenster und wirkt sofort. foldlevelstart legt fest, mit welcher Faltungstiefe eine Datei geöffnet wird .

Wenn du also möchtest, dass eine Datei beim Öffnen komplett aufgeklappt ist, setzt du foldlevelstart=99 in deiner ~/.vimrc .

Wichtige Befehle

  • :set foldlevel? zeigt den aktuellen Wert an
  • :setlocal foldlevel=1 setzt die Tiefe nur für das aktuelle Fenster
  • 2zM schliesst alle Faltungen bis Ebene 2, lässt Ebene 1 offen

Faltungen dauerhaft speichern

Ein wichtiger Punkt, den viele nicht wissen. Wenn du eine Datei schliesst, gehen manuell erstellte Faltungen verloren. Vim merkt sich den Zustand nicht automatisch.

Dafür gibt es die Befehle :mkview und :loadview. Mit :mkview speicherst du den aktuellen Zustand inklusive der Faltungen. Wenn du die Datei später wieder öffnest, lädst du mit :loadview alles zurück. Du kannst bis zu zehn verschiedene Ansichten pro Datei speichern.

Speicherort Faltungen

Vim speichert die Ansichten in dem Verzeichnis, das in der Option viewdir definiert ist. Der Standardwert hängt von deinem Betriebssystem ab:

  • Linux und Unix (auch macOS): ~/.vim/view
  • Windows: $VIM/vimfiles/view

In diesem Ordner legt Vim für jede Datei eine eigene View-Datei ab. Der Dateiname wird dabei aus dem Pfad der Originaldatei generiert, wobei Sonderzeichen durch Gleichheitszeichen (=) ersetzt werden .

Den aktuellen Speicherort herausfinden

Du kannst dir den Pfad direkt in Vim anzeigen lassen:

:set viewdir?

Die Ausgabe zeigt dir, wo Vim aktuell sucht oder speichert .

Speicherort ändern

Wenn du den Standardpfad ändern möchtest (zum Beispiel um dein Home-Verzeichnis sauber zu halten), kannst du die Option in deiner ~/.vimrc anpassen:

set viewdir=~/.vim/meine-views

Bei neueren Vim-Versionen respektiert der Standardwert mittlerweile auch die XDG_CONFIG_HOME-Umgebungsvariable und nutzt dann ~/.config/vim/view .

View-Dateien löschen

Da es keinen eingebauten Befehl zum Löschen gibt, musst du die Dateien manuell entfernen. Navigiere dazu einfach in den oben genannten Ordner und lösche die entsprechenden Dateien .

Die wichtigsten Befehle für den Alltag

Die grundlegenden Befehle solltest du kennen, dann kommst du schon sehr weit.

Öffnen und Schliessen

  • zo öffnet eine Faltung unter dem Cursor (open)
  • zc schliesst eine Faltung unter dem Cursor (close)
  • za klappt die Faltung um, öffnet wenn geschlossen, schliesst wenn offen (alternate)
  • zR öffnet alle Faltungen im Dokument (Reset)
  • zM klappt alle Faltungen zu im Dokument (Minimize)

Navigation zwischen Faltungen

Kombination mit den klassischen Navigationsbefehlen:

  • zj springt zur nächsten Faltung
  • zk springt zur vorherigen Faltung
  • [z springt zum Anfang der aktuellen Faltung
  • ]z springt zum Ende der aktuellen Faltung

Erstellen und Löschen

  • zf gefolgt von einer Bewegung erstellt eine Faltung, zum Beispiel zfap für einen ganzen Absatz (fold)
  • zd löscht die Faltung unter dem Cursor, der Text bleibt erhalten (delete)
  • zE löscht alle Faltungen im Fenster (Eliminate)

Praktische Beispiele - Faltungsmethoden

Vim bietet sechs verschiedene Methoden, um Faltungen zu erstellen. Welche du wählst, hängt davon ab, was du bearbeitest und wie viel Kontrolle du haben möchtest.

manual - Manuelles Falten mit zf

Die direkteste Methode. Du markierst einen Bereich und faltest ihn mit zf. Das funktioniert visuell, mit Bewegungen oder mit Klammern.

Visuell markieren und falten

  1. Markiere den Bereich mit v (zeichenweise), V (zeilenweise) oder Strg-v (blockweise)

  2. Drücke zf

Vim erstellt daraus eine Faltung. Der Text bleibt erhalten, wird nur versteckt .

Mit Bewegungen falten

  • zfap faltet einen ganzen Absatz (absatz packen - a paragraph)
  • zfG faltet vom Cursor bis zum Ende der Datei (Ganz unten - Go to end)
  • zfgg faltet vom Dateianfang bis zum Cursor (geh ganz hoch)

Zwischen Klammern falten

Der eleganteste Weg für Code. Setz den Cursor auf eine öffnende Klammer wie { oder [ und drücke zf%. Vim springt zur passenden schliessenden Klammer und faltet alles dazwischen .

Ein Beispiel in einer JavaScript-Datei:

function berechneSumme(a, b) {
  const ergebnis = a + b;
  console.log(ergebnis);
  return ergebnis;
}

Mit dem Cursor auf der öffnenden { und zf% sieht es danach so aus:

+-- 4 lines: function berechneSumme(a, b) {

indent - Faltung durch Einrückung

Für Code die praktischste Methode. Vim erstellt automatisch Faltungen basierend auf der Einrückungstiefe .

:set foldmethod=indent

Jede Ebene der Einrückung wird zu einer Faltungsebene. Beim Öffnen einer Datei mit foldlevel=1 sind alle Funktionen eingeklappt und du siehst nur die Struktur auf einen Blick.

marker - Faltung durch Marker

Du setzt spezielle Markierungen in den Text, die Vim als Faltungsgrenzen erkennt. Die Standard-Marker sind {{{ und }}} .

:set foldmethod=marker

Ein Beispiel:


<div class="container">
  <p>Erste Zeilep>
  <p>Zweite Zeilep>
div>

Der Text zwischen den Markern wird zu einer Faltung. Der Vorteil: Die Faltung bleibt beim erneuten Öffnen der Datei erhalten. Der Nachteil: Du veränderst die Datei selbst.

syntax - Faltung durch Syntax

Vim nutzt die vorhandenen Syntax-Regeln, um Faltungen zu erstellen. Für die meisten Programmiersprachen sind diese Regeln bereits vorhanden .

:set foldmethod=syntax

Bei Ruby-Code werden beispielsweise alle Methoden automatisch gefaltet. Du musst nichts weiter konfigurieren.

expr - Faltung durch Ausdruck

Die flexibelste Methode. Du definierst einen Ausdruck, der für jede Zeile entscheidet, ob sie gefaltet wird .

:set foldmethod=expr
:set foldexpr=getline(v:lnum)=~'ERROR'

Dieses Beispiel faltet alle Zeilen, die nicht das Wort ERROR enthalten. Ideal für Log-Dateien, um nur die Fehler sichtbar zu lassen.

diff - Faltung durch diff

Wenn du zwei Versionen einer Datei vergleichst, werden unveränderte Bereiche automatisch gefaltet. Diese Methode wird meist automatisch aktiviert, wenn du vimdiff oder :diffthis verwendest.

Praktische Anwendung

Für Code-Dateien nutze ich meist foldmethod=indent in Kombination mit foldlevel=1. Dann sind beim Öffnen einer Datei alle Funktionen eingeklappt und ich sehe nur die Struktur auf einen Blick. Mit za klappe ich dann die Funktion auf.

Für längere Textdokumente ist foldmethod=manual oft besser. Dann kann ich selbst entscheiden, welche Absätze ich zusammenklappe.

Und wenn dir die Faltungen mal zu viel werden, dann hilft zR oder zn, um alles wieder zu öffnen.

Quellen

Vim ist als Open-Source-Software verfügbar.

Alte Views löschen - Vimscript

Hier ist ein Vimscript, das verwaiste View-Dateien findet und löscht, sowie eine Anleitung zum Einbinden.

Das Skript nutzt die Funktionen readdir() zum Auflisten des View-Ordners und delete() zum Löschen der Dateien . Die Umkehrung der Namenskonvention (=+ zu /) ist der Kern der Prüfung.

Die Umkehrung der Namenskonvention

Das ist der entscheidende Punkt. Vim speichert View-Dateien nicht unter dem Originalnamen, sondern kodiert den vollständigen Pfad in den Dateinamen. Dabei wird jeder Schrägstrich (/) durch die Zeichenfolge =+ ersetzt.

Ein Beispiel. Die Originaldatei /home/user/test.txt wird zu einer View-Datei mit dem Namen =+home=+user=+test.txt im View-Ordner.

" Funktion zum Bereinigen verwaister View-Dateien
function! s:CleanOrphanedViews()
    " Ermittle das View-Verzeichnis (Standard: ~/.vim/view)
    let view_dir = expand(&viewdir)
    
    " Prüfe, ob das View-Verzeichnis existiert
    if !isdirectory(view_dir)
        echohl WarningMsg
        echo "View-Verzeichnis nicht gefunden: " . view_dir
        echohl None
        return
    endif

    let orphaned_count = 0
    
    " Alle Dateien im View-Verzeichnis auflisten
    for view_file in readdir(view_dir)
        let view_path = view_dir . '/' . view_file
        
        " Nur Dateien verarbeiten (keine Unterordner)
        if !filereadable(view_path)
            continue
        endif

        " Den Dateinamen zurück in einen Pfad umwandeln
        " Vim ersetzt '/' durch '=+' im Dateinamen
        let original_path = substitute(view_file, '=+', '/', 'g')
        
        " Prüfen, ob die Originaldatei noch existiert
        " filereadable() ist robuster als glob() bei Berechtigungsproblemen 
        if !filereadable(original_path)
            " Datei existiert nicht mehr -> View löschen
            if delete(view_path) == 0
                let orphaned_count += 1
                echom "Verwaiste View gelöscht: " . view_file
            else
                echohl WarningMsg
                echom "Fehler beim Löschen: " . view_file
                echohl None
            endif
        endif
    endfor

    " Zusammenfassung ausgeben
    if orphaned_count > 0
        echom "Bereinigung abgeschlossen: " . orphaned_count . " verwaiste View(s) gelöscht."
    else
        echom "Keine verwaisten View-Dateien gefunden."
    endif
endfunction

" Befehl zum Aufrufen der Funktion definieren
command! CleanViews call s:CleanOrphanedViews()

Wie das Skript funktioniert

  1. Verzeichnis ermitteln: Das Skript liest die globale Option &viewdir aus, die standardmäßig auf ~/.vim/view (Unix) oder $VIM/vimfiles/view (Windows) zeigt.
  2. Dateien auflisten: Mit readdir() werden alle Einträge im View-Ordner gelesen .
  3. Pfad rekonstruieren: Der entscheidende Schritt. Vim kodiert den Originalpfad im View-Dateinamen, indem es jeden Schrägstrich (/) durch die Zeichenfolge =+ ersetzt. Die Funktion substitute() macht diese Ersetzung rückgängig.
  4. Existenzprüfung: filereadable() prüft, ob die so rekonstruierte Originaldatei noch vorhanden und lesbar ist . Dies ist robuster als eine reine glob()-Prüfung, falls Berechtigungen im Spiel sind .
  5. Löschen: Wenn die Originaldatei fehlt, wird die View-Datei mit der eingebauten delete()-Funktion entfernt .

Einbindung in die Vim-Konfiguration

Du hast zwei Möglichkeiten, das Skript dauerhaft zu nutzen.

Option 1: Direkt in die .vimrc einfügen

Kopiere den gesamten Codeblock in deine ~/.vimrc (oder ~/.config/nvim/init.vim). Nach dem Speichern und Neustarten von Vim kannst du den Befehl :CleanViews verwenden.

Option 2: Als separates Plugin (empfohlen für Ordnung)

  1. Erstelle eine Datei namens cleanviews.vim im Verzeichnis ~/.vim/plugin/.
  2. Füge den Code dort ein.
  3. Vim lädt Dateien in ~/.vim/plugin/ beim Start automatisch.

In beiden Fällen steht der Befehl :CleanViews zur Verfügung. Er gibt nach dem Lauf eine Meldung aus, wie viele verwaiste View-Dateien gelöscht wurden.

21. September 2026

Mit Common Voice stellt Mozilla den weltweit größten öffentlichen Datensatz menschlicher Stimmen bereit – kostenlos und für jeden nutzbar. Mozilla hat Version 27 seines Datensatzes veröffentlicht.

Der Markt für Spracherkennung wird von den ganz großen Namen kommerzieller Anbieter dominiert: Amazon, Apple, Google, Microsoft. Darum hat Mozilla im Jahr 2017 das Projekt Common Voice gestartet. Mit Common Voice bietet Mozilla eine kostenlose Alternative an, zu der jeder beitragen kann und die jedem zur Verfügung steht. Damit möchte Mozilla Innovation und Wettbewerb in der Sprachtechnologie auf Basis von Maschinenlernen fördern.

Mozilla Common Voice 27

Der nun veröffentlichte Datensatz Common Voice Scripted Speech 27 beinhaltet für die deutsche Sprache 1.492 Stunden an Daten und ist 34,82 GB groß. In Summe waren 20.558 Menschen am deutschsprachigen Datensatz beteiligt. Der Datensatz Common Voice Spontaneous Speech 5 für spontane Sprache kommt für Deutsch auf 1,47 Stunden an Daten und ist 38,71 MB groß, beigetragen von 28 Personen.

Insgesamt deckt Mozilla Common Voice mit der neuen Version, die wieder Unterstützung für eine neue Sprache bringt, 295 Sprachen mit insgesamt 42.593 aufgenommenen Stunden ab, was Mozilla Common Voice zum vielfältigsten mehrsprachigen Sprachkorpus der Welt macht. Die Anzahl der unterstützten Sprachen für spontane Sprache ist von 78 auf 80 Sprachen gewachsen.

Zum Download der Mozilla Common Voice Datensätze
Zu Mozilla Common Voice beitragen

Der Beitrag Mozilla veröffentlicht Common Voice 27 erschien zuerst auf soeren-hentzschel.at.

Dieser Post umfasst eine kurze Problembeschreibung und verweist auf die Lösung, die ich dazu im Internet gefunden und implementiert habe.

Er dient mir als Dokumentation und ich freue mich, wenn er euch ebenfalls hilft, wenn ihr von dem gleichen Problem betroffen seid.

Problembeschreibung

Ein Server mit einer Intel-Netzwerkkarte, welche den Treiber e1000e nutzt, verliert plötzlich die Netzwerkanbindung. Im Protokoll des Servers finden sich dazu wiederholt folgende Meldungen:

kernel: e1000e 0000:00:1f.6 eno1: Detected Hardware Unit Hang:

Remote-Verbindungen werden unterbrochen und es ist nur noch ein Zugriff über eine lokale Konsole möglich. Das kann unpraktisch sein, wenn sich der Server in einem hunderte Kilometer entfernten Standort befindet.

Lösung

Die Lösung habe ich bei der Internetrecherche im Blog von Ruhani Rabin gefunden: https://rabin.blog/fix-intel-e1000e-detected-hardware-unit-hang-on-proxmox/#solution-summary-proxmox-intel-e1000e-hardware-unit-hang-fix

Ruhani hat eine systemd.unit erstellt, welche die problematischen Funktionen des Treibers deaktiviert. Zusätzlich wird ein Watchdog eingerichtet, welcher helfen soll, das Problem bei erneutem Auftreten selbst zu lösen.

Bitte schaut in Ruhanis Blog-Artikel für alle Details zur Lösung. Er hat diese wirklich gut beschrieben.

Ich dokumentiere in den folgenden Code-Blöcken lediglich die systemd.units, die ich auf meinem Host implementiert habe:

# /etc/systemd/system/disable-nic-offload-eno1.service

[Unit]
Description=Disable NIC offloading for Intel e1000e interface eno1
After=network-pre.target
Wants=network-pre.target

[Service]
Type=oneshot
ExecStart=/sbin/ethtool -K eno1 gso off gro off tso off tx off rx off rxvlan off txvlan off sg off
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Und der Watchdog:

# /etc/systemd/system/e1000e-eno1-watchdog.timer
[Unit]
Description=Run e1000e eno1 watchdog every 2 minutes

[Timer]
OnBootSec=2min
OnUnitActiveSec=2min
AccuracySec=30s
Unit=e1000e-eno1-watchdog.service

[Install]
WantedBy=timers.target

# /etc/systemd/system/e1000e-eno1-watchdog.service
[Unit]
Description=Watch for Intel e1000e hardware hangs and reset eno1
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/e1000e-eno1-watchdog

Watchdog-Skript:

#!/usr/bin/env bash
set -u
set -o pipefail

IFACE="eno1"
LOOKBACK="5 minutes ago"
LOCK="/run/e1000e-${IFACE}-watchdog.lock"
STAMP="/run/e1000e-${IFACE}-last-reset"
MIN_RESET_INTERVAL_SECONDS=600

exec 9>"$LOCK"
flock -n 9 || exit 0

if ! journalctl -k --since "$LOOKBACK" --no-pager \
  | grep -Eqi "e1000e .*${IFACE}: Detected Hardware Unit Hang|Detected Hardware Unit Hang"; then
  exit 0
fi

now="$(date +%s)"
last=0
[ -f "$STAMP" ] && last="$(cat "$STAMP" 2>/dev/null || echo 0)"

if [ $((now - last)) -lt "$MIN_RESET_INTERVAL_SECONDS" ]; then
  logger -t e1000e-watchdog "hardware hang detected on ${IFACE}, but reset suppressed by rate limit"
  exit 0
fi

logger -t e1000e-watchdog "hardware hang detected on ${IFACE}; resetting NIC"

if ! ip link set "$IFACE" down; then
  logger -t e1000e-watchdog "failed to bring ${IFACE} down; NIC reset incomplete"
  exit 1
fi

sleep 2

if ! ip link set "$IFACE" up; then
  logger -t e1000e-watchdog "failed to bring ${IFACE} up; NIC reset incomplete"
  exit 1
fi

# Re-apply the offload mitigation after link reset, just in case.
ethtool -K "$IFACE" gso off gro off tso off tx off rx off rxvlan off txvlan off sg off 2>/dev/null || \
  logger -t e1000e-watchdog "warning: unable to re-apply offload mitigation on ${IFACE}"

date +%s >"$STAMP"

logger -t e1000e-watchdog "NIC reset completed for ${IFACE}"

Bugtracker

Laut des KI-Agenten meines geringsten Misstrauens ist das Problem in folgenden Trackern dokumentiert:

Zum Erscheinungsdatum dieses Beitrags befinden sich beide im Status ‚NEW‘.

19. September 2026

Wer auf fryboyter.de aktuell die Suchfunktion verwendet, lädt erst einmal eine JSON-Datei herunter. An sich eine feine Sache, da die Suche somit lokal erfolgt. Allerdings wird diese Index-Datei immer größer. Aktuell über einem Megabyte.

Ich habe mich daher schon vor längerer Zeit überlegt eine andere Lösung zu nutzen. Schlussendlich bin ich bei Pagefind gelandet. Der damit erzeugte Index besteht nicht aus einer großen Datei, sondern aus mehreren kleinen Dateien von denen nur die geladen werden die für die jeweilige Suche benötigt werden. Es spart also Bandbreite für den jeweiligen Nutzer.

Mit Pagefind gab es bei meinen bisherigen Tests allerdings ein Problem. Die Artikelüberschrift wurde zweimal genannt. Einmal als Titel des Suchergebnisses in Form eines Links. Und einmal sozusagen im angezeigten Artikeltest. Also beispielsweise so.

Neuer Betreuer für das Hugo-Theme Ananke gesucht

Betreuer für das Hugo-Theme Ananke gesucht. Blablabla. Veröffentlicht am 16. September 2024 |

Das ist jetzt nicht unbedingt schlimm aber es ist irgendwie nervig. Eine Lösung hatte ich bisher nicht gefunden. Was aber an mir und nicht an Pagefind lag. Im Grunde ist die Lösung ziemlich einfach. Bisher sieht die Datei single.html, mit der die einzelnen Artikel angezeigt werden, wie folgt aus.

{{ define "head" }}
{{ partial "head.html" . }}
{{ end }}
{{ define "main" }}

{{ .Title }}

{{ partial "metadata.html" . }} {{- .Content }} {{- partial "taxonomy.html" . }} {{- partial "related.html" . }}
{{ end }}

Ändert man diese Datei nun folgendermaßen, wird die Überschrift nur einmal angezeigt, so wie es sein soll.

{{ define "head" }}
{{ partial "head.html" . }}
{{ end }}
{{ define "main" }}

="title">{{ .Title }}

{{ partial "metadata.html" . }}
{{- .Content }}
{{- partial "taxonomy.html" . }} {{- partial "related.html" . }}
{{ end }}

Entscheidend ist hierbei in dem Fall hauptsächlich, dass man der Artikelüberschrift data-pagefind-meta=“title” zuweist und erhält dann beispielsweise folgende Anzeige.

Neuer Betreuer für das Hugo-Theme Ananke gesucht

der MIT-Lizenz) für den statischen Website-Generator Hugo. Für dieses Theme wird nun

Einige werden sich nun fragen, warum der Artikeltext im Suchergebnis mit “der MIT-Lizenz)” anfängt. Pagefind zeigt in der Standardkonfiguration die Zeile an, in der der Suchbegriff das erste Mal erscheint. Ich bin mir nicht sicher, ob mir das so gefällt.

Alternativ könnte man auch das anzeigen, was im Tag steht. Was ich für wesentlich besser halte. Nur habe ich diesen Eintrag bisher nie erstellt. Also seit 2009. Das nachzuholen würde einige hundert Artikel betreffen. Worauf ich manuell keine Lust habe. Daher bin ich aktuell dabei eine Lösung zu testen die mir anhand zweier lokaler LLM eine sinnvolle Beschreibung liefert. Welche ich aber selbstverständlich prüfen werde, falls ich diese Lösung nutzen werde. Aber das ist Stoff für einen anderen Artikel. Vielleicht.

18. September 2026

Was lässt sich nach knapp einem halben Jahr über NixOS sagen? Ist es für den "Normalo" zu schwierig? Wie performt es?



Vor einem halben Jahr bin ich zu NixOS gewechselt und nutze es auf allen meinen Rechnern. Zeit für eine ehrliche Zwischenbilanz.

Das Fazit zuerst

Starkoch Nixos kocht noch immer. Die Küche ist aufgeräumt, die Speisekarte als einzige Quelle der Wahrheit ist der Königsweg – und das in drei Restaurants gleichzeitig.

Die beste Erfahrung: NixOS hat mich in den letzten sechs Monaten nie im Stich gelassen, obwohl ich nicht zimperlich mit ihm umgegangen bin. Kein einziger Paketkonflikt, obwohl ich fast alles aus dem Unstable-Branch beziehe – kein zerschossenes System, obwohl ich massiv an der Konfiguration gebastelt habe – der Grundstock ist nach wie vor die Erstinstallation. Ich hatte wirklich immer einen voll arbeitsfähigen Rechner, was mich zwar maximal erfreute, aber es irritierte mich zu Beginn auch irgendwie. Denn nach meiner bisherigen Erfahrung mit Basteln an Linux wäre so stümper- und laienhaftes Schrauben am System unweigerlich mit einem einsam blinkendem Curser auf schwarzem Grund bestraft worden. Mittlerweile ist es mir zur beruhigenden Gewissheit geworden, dass ich immer auch einen Schritt zurück machen kann (1-Klick-Rollback beim Reboot), sollte die neue Konfiguration noch nicht passen.

Ich hatte Probleme, nicht wenige(!), aber diese hatten weniger mit NixOS, als überwiegend mit Git bzw. Codeberg zu tun. NixOS ist ein absolut großartiges Desktop-Linux, aber noch beeindruckender sind die Möglichkeiten, die diese Distribution für eine Multi-Host-Umgebung (also eine Nix-Konfiguration für mehrere Rechner) bietet. Ich hatte die dahingehenden Möglichkeiten von Flakes in Teil 2, Teil 3 und einem weiteren Beitrag in meinem Setup-Guide beschrieben. An Git führt dann aber kein Weg vorbei. Nur ... – Git ist so eine Bit**, es duldet nur die 100%ige und vollständige Unterwerfung vor den Regeln – schlecht, wenn man diese noch nicht so drauf hat. Zum Glück ist NixOS selbst aber extrem robust und stabil, es hat sich durch meine wilde Lernkurverei nie aus der Bahn werfen lassen.

Wenn ich in NixOS mehr verändern möchte, als nur ein Paket zu installieren, schaue ich immer zuerst in die Dokumentation (Wiki und die Options), damit ich den Grundgedanken der Konfiguration in der Nix-Sprache verstehe. Das Suchen in Foren war immer eine Sackgasse. Dagegen lasse ich mir meinen Entwurf für veränderte Konfigurationsdateien (.nix, ich nenne sie liebevoll "Nixen") immer von einer freundlichen KI gegenchecken. Gleiches gilt für die Interpretation von Fehlermeldungen, welche ich oft als Reaktion auf meine Experimente zurückbekam. Diese Fehlermeldungen (generiert vom Nix-Evaluator) sind übrigens bemerkenswert, da sie ziemlich ausführlich, konkret und präzise ausfallen, keineswegs nur eine Fehlernummer mit Benennung. Mit dem beschriebenen Vorgehen bin ich als Novize gut gefahren.

Ich schrieb ja schon ziemlich zu Beginn, also voll in meiner Anfangseuphorie, dass Distro-Hopping durch NixOS ein Ende gefunden hat. Daran hat sich tatsächlich nichts geändert, nach einem halben Jahr mit NixOS gibt es kein Zurück mehr. Die Idee des Deklarativen (siehe Teil 1 meines Setup-Guides) hat sich eingebrannt und begeistert mich bis heute ungebrochen.

Was brillant funktioniert

Das Versprechen der Reproduzierbarkeit. Ich betreibe mittlerweile drei Rechner mit NixOS: einen Dell 5320, einen Dell Pro Plus und ein Surface Go 2 (welches mein altes Lenovo ersetzte). Alle drei laufen mit der gleichen Basis-Konfiguration. Wenn ich auf einem Rechner etwas ändere, landen die Änderungen via Codeberg auf allen anderen – mit einem einzigen Befehl. Das klingt nach einem kleinen Kunststück – aber genau das ist NixOS.

Rollbacks retten Leben. Zweimal bootete ein Rechner nach einem Rebuild (mit von mir verursachten Konfigurationsfehlern) nicht mehr – einmal der Dell 5320 wegen einer falschen UUID in der hardware-configuration.nix, einmal das Surface wegen eines von mir verursachtem Kernel-Konflikts. Beide Male: Neustart – ältere Generation im Bootmenü wählen – fertig. Auf anderen Systemen wäre das eine Neuinstallation geworden. Bei NixOS war es ein Klick. Und das ist schon beeindruckend – und ungemein beruhigend. Der Arbeitstag kann dann in jedem Fall erstmal normal weiter laufen.

Das Update-Skript. Von Beginn an erstellte ich mir kleine Skripte für die Shell (in meinem Fall die Fish-Shell), welche Arbeitsschritte zusammenfassen. Klar, ein Update funktioniert bei NixOS tatsächlich mit nur einem Befehl, aber wenn man wie in meinem Fall Flakes nutzt, muss es ja noch über Git zu Codberg und committet werden. Was als einfaches nixos-rebuild switch begann, ist heute eine vollständige Fish-Funktion namens update, die automatisch erkennt, ob ein Rechner hinter Codeberg zurückliegt oder lokale Änderungen hat – und dann das Richtige tut. Kein Nachdenken mehr darüber, ob ich eine aktualisierte Konfiguration auch wirklich via Git zu Codeberg transferiert hatte.
Konkret funktioniert das so: Mein Skript update vergleicht zunächst den lokalen Git-Stand mit dem auf Codeberg. Hängt der aktuelle Rechner hinter Codeberg zurück – zum Beispiel, weil auf einem anderen Rechner Änderungen gemacht wurden – holt es automatisch den aktuellen Stand und baut das System neu. Hat der aktuelle Rechner hingegen neue lokale Änderungen, aktualisiert es die Paketversionen, baut das System, bereinigt den Nix-Store und sichert alles auf Codeberg. Sind beide Seiten auf dem gleichen Stand, wird trotzdem ein Update der Pakete angestoßen – damit kein Rechner aus Versehen veraltet.
Darunter liegen drei Funktionen, die ich zunächst als separate Skripte hatte: update-push für das bewusste Aktualisieren und Sichern, pull-update für das Holen und Bauen vom Codeberg-Stand, und rebuild für den schnellen lokalen Neuaufbau ohne Git.

Und die Weiterentwicklung in Form des Skripts update steht auch Dir zur Verfügung. Die verlinkten Konfigurationsdateien aus dem Teil 2 meines Setup-Guides habe ich seither aktualisiert.

Multi-Host-Setup. Durch das mehrfach verbesserte und nun finale Update-Skript konnte ich meine selbst- und hausgemachten Git- und Codeberg-Probleme überwinden. Seither ist die Freude an der Multi-Host-Performance von NixOS ungetrübt. Kleines Beispiel: Ich hatte meine Tochter immer um ihr schickes kleines Surface Go 3 beneidet, deshalb kaufte ich mir nach dem Tod meines alten Lenovo (ich berichtete HIER von der Einbindung) ein gebrauchtes Go 2 mit M3-Prozesser. Unter den Ultra-Portablen PCs mit Tablet-Funktion, ist die Surface-Go-Serie in meinen Augen ein herausragend wunderbares Stück Hardware – einen kleinen portablen Rechner brauche ich einfach auch (hauptsächlich für den Urlaub). Trotz einer abgespeckten Programmauswahl und weniger Verknüpfungen mit Netzwerklaufwerken, lässt sich der kleine Rechner dennoch super in die gemeinsame Grundkonfiguration einbinden und auch dort verwalten. Die oben verlinkte Anleitung beschreibt das Vorgehen. Die für das Go 2 angepassten "Nixen" verlinke ich ganz unten.

Performance. Beispielsweise das erwähnte Surface Go 2 (mit dem M3-Prozessor) läuft mit dem Standard-NixOS-Kernel (einen angepassten Surface-Kernel braucht es nicht) unter NixOS um Welten flüssiger, als das leicht bessere Go 3 meiner Tochter mit dem i3 unter Ubuntu.
Generell muss ich sagen, dass NixOS extrem flüssig auf jeder meiner Maschinen läuft. Auf dem Dell5320 hatte ich ja vorher CachyOS, das lief keine Spur besser.

Wo es wirklich gehapert hat

Git und Codeberg waren mein Erzfeind, mein Endgegner, "aarrrrghh". Der SSH-Key für root fehlte. Branches liefen auseinander. Push-Fehler wegen nicht-linearer Historie. Falsche UUIDs in der hardware-configuration.nix die irgendwann beim falschen Rechner gelandet waren. Ich habe mindestens ein Dutzend Mal sudo git reset --hard origin/master eingegeben – manchmal notgedrungen, manchmal weil ich es schlicht vergessen hatte vorher zu pullen. Aus diesen selbst eingebrockten Frust-Momenten entstand das Skript update, welches mich vor eigenen Fehlern im Umgang mit Git schützt.

Die ehrliche Diagnose: Die Git-Workflows bei einem Multi-Host-Setup sind nicht trivial. Man muss verstehen, dass Codeberg die Quelle der Wahrheit ist – und immer dann, wenn man das vergisst, rächt es sich. Ich habe es oft vergessen.

Die flake.nix ist nicht fehlerverzeihend. Ein fehlendes Anführungszeichen, ein falscher Attributname, ein home-manager.users.retorix das für zwei von drei Rechnern vergessen wurde – und das System baut nicht, oder schlimmer, es baut aber lässt danach alle Programme verschwinden. Der Nix-Evaluator gibt aber geduldig so lange erläuternde Fehlermeldungen, bis man den Dreh (evtl. mit KI-Unterstützung) raus hat.

Der fehlerhafte Nextcloud-Client. Ausgerechnet der für mich so wichtige Nextcloud-Client machte als normales Paket Probleme. Das selektive Auswählen von Synchronisationsordnern war im Paket fehlerhaft. Die Internet-Recherche ergab, dass es ein bekanntes Problem ist, die Flatpak-Version aber fehlerfrei sein soll. Somit musste ich in den sauren Apfel beißen (ich mag keine Container) und nur für dieses eine Programm auf meinem System Flathub einrichten. Tatsächlich waren damit dann aber auch die Probleme mit dem Nextcloud-Client gelöst.

Zeitaufwand für ein Update. Das ist nur eine Anmerkung, nichts was einschränkt, oder gar störend ist – ich möchte es aber erwähnen. Da NixOS ja bei jedem Update den Nix-Store neu sortiert, das System nach der geänderten Deklaration neu baut und den alten Ballast von Bord wirft, dauert ein Update länger, als z.B. ein sudo pacman -Syu auf einem Arch-Linux-System. Parallel weiterarbeiten ist aber kein Problem. Dafür ist das System aber stets frisch und clean gebaut, so als wäre es eine Neuinstallation.

Was ich gelernt habe

NixOS ist nicht das superschwierige Nerd-System. Die Konfigurationsdateien folgen einer glasklaren Logik, die man einmal verstehen muss – und dann läuft es, auch wenn es zu Beginn manchmal ruckelt.
Was ich unterschätzt hatte: die Werkzeuge rund um NixOS (Git, Codeberg, Flakes, Home-Manager) bilden zusammen ein Ökosystem, das man erst kennenlernen muss – da stecken die steilen Rampen in der Lernkurve - aber eben nur dann, wenn man ein Multi-Host-Setup anstrebt.

In Teil 2 schrieb ich:

Es gibt viele technische Gründe für die "Gewaltenteilung" im System, mich persönlich überzeugt aber ein eher philosophischer Grund am meisten: Semantische Klarheit: configuration.nix beschreibt, was das System ist, home.nix beschreibt, was Du bist. Das ist keine technische Notwendigkeit, sondern eine gedankliche Ordnung, die sich auszahlt, sobald die Konfiguration dann doch mal wächst.

Dazu stehe ich auch heute noch, würde aber nach meinem wilden Ritt mit Git und Codeberg heute dazu anmerken:
Gib der deklarativen Idee von NixOS unbedingt eine Chance, bleib aber bei nur einem oder zwei Rechnern erstmal bei der ausschließlichen Konfiguration über die configuration.nix, so wie in Teil 1 meiner Setup-Reihe beschrieben.

Wenn jemand aufgrund meiner Impulse ebenfalls bei NixOS gelandet ist, würde ich mich über einen Kommentar freuen; aber jeder andere Beitrag zu NixOS interessiert mich natürlich ebenso.

Ressourcen

Hier die Konfigurationsdateien für das Go 2. In der flake.nix (siehe Download in Teil 2) ist das Surface natürlich als Teil des Multi-Host-Setups ebenfalls mit aufgeführt:
surface-system.nix
home-surface.nix


GNU/Linux.ch ist ein Community-Projekt. Bei uns kannst du nicht nur mitlesen, sondern auch selbst aktiv werden. Wir freuen uns, wenn du mit uns über die Artikel in unseren Chat-Gruppen oder im Fediverse diskutierst. Auch du selbst kannst Autor werden. Reiche uns deinen Artikelvorschlag über das Formular auf unserer Webseite ein.

17. September 2026

Die MZLA Technologies Corporation hat mit Thunderbird 156 eine neue Version seines Open Source E-Mail-Clients für Windows, Apple macOS und Linux veröffentlicht.

Neuerungen von Thunderbird 156

Mit Thunderbird 156 hat die MZLA Technologies Corporation ein Update für seinen Open Source E-Mail-Client veröffentlicht. Die neue Version bringt Verbesserungen für bestimmte Authentifizierungs-Szenarien in Zusammenhang mit Yandex sowie OAuth. Debug-Informationen für PGP lassen sich in der Browserkonsole ausgeben. Die Unterstützung für mehrere neue Unternehmensrichtlinien wurde ergänzt. Ansonsten gab es auch wieder eine ganze Reihe von Verbesserungen unter der Haube und Fehlerkorrekturen, welche sich wie immer in den Release Notes (engl.) nachlesen lassen. Auch Sicherheitslücken wurden im neuesten Update wieder behoben.

Der Beitrag Thunderbird 156 veröffentlicht erschien zuerst auf soeren-hentzschel.at.

16. September 2026

Mozilla und Mistral haben gemeinsam eine Partnerschaft angekündigt, um offene, private und mehrsprachige KI für diejenigen Nutzer in den Browser zu bringen, die KI im Browser nutzen wollen. Dabei setzt Mozilla natürlich, wie auch bisher, auf eine optionale und freiwillige Nutzung.

Wie Mozilla und das französische KI-Unternehmen Mistral gemeinsam angekündigt haben, arbeitet man für KI-Funktionen in Firefox zusammen. Konkret geht es dabei um die sogenannten intelligenten Fenster, die bereits schrittweise für Nutzer in den USA, Kanada und Frankreich ausgerollt werden. Über eine eigene Oberfläche können unter anderem komplexe Recherchen zusammengefasst, zuvor besuchte Inhalte wiedergefunden und Informationen aus geöffneten Tabs aufbereitet werden. Eine Ausrollung in Deutschland sowie Großbritannien soll noch in diesem Jahr folgen.

Mozilla stellt für die intelligenten Fenster unter anderem Mistral Small 4 als Modell zur Verfügung. Das Besondere an Mistral: Die Modelle von Mistral sind genau wie Firefox Open Source. Sprache, Dialekte und kulturelle Besonderheiten werden in den Modellen von Mistral von Beginn an berücksichtigt, statt eine primär englischsprachige Lösung lediglich nachträglich zu übersetzen. Und auch der Datenschutz ist ein wichtiger Aspekt: Unterhaltungen werden nicht auf Mozilla-Servern gespeichert und Mistral verpflichtet sich zur Nichtaufbewahrung übermittelter Daten.

Der Beitrag Mozilla und Mistral kündigen Partnerschaft an erschien zuerst auf soeren-hentzschel.at.

15. September 2026

Mozilla hat Firefox 156 für Windows, Apple macOS und Linux veröffentlicht. Dieser Artikel fasst die wichtigsten Neuerungen zusammen.

Download Mozilla Firefox für Microsoft Windows, Apple macOS und Linux

Neuerungen in Firefox 156

Wie auf Windows gibt es nun auch auf macOS eine Einstellung, die aktiviert werden kann, damit Firefox mit dem Starten des Systems direkt mit gestartet werden kann.

In den Einstellungen für die Tab-Umgebungen wurde die neue Option „Keine Tab-Umgebungen für Links verwenden, die von externen Apps geöffnet werden” hinzugefügt.

Auch in Deutschland, Frankreich und Italien können nun direkt in der Adressleiste Vorschläge von Wikipedia oder gesponserte Vorschläge angezeigt werden, was in den Einstellungen zur Suche ein- und ausgeschaltet werden kann.

Der PDF-Betrachter startet jetzt bis zu 45 Prozent schneller. Die Nutzung von CPU und RAM wurde verbessert, wenn große JPEG-Dateien herunterskaliert werden. Und auf Geräten mit ARM64-CPU und Windows gibt es jetzt Hardware-Unterstützung für das Decoding von H.264 in WebRTC-Kommunikation.

Auch in Firefox 156 wurden wieder mehrere Sicherheitslücken geschlossen. Alleine aus Gründen der Sicherheit ist ein Update auf Firefox 156 daher für alle Nutzer dringend empfohlen.

Darüber hinaus gab es diverse Korrekturen, welche in den offiziellen Release Notes näher beschrieben werden, darunter auch das Problem, dass Firefox für manche Nutzer in Zusammenhang mit dem integrierten VPN oder DNS-over-HTTPS Probleme beim Laden von Websites haben konnte. Verbesserungen der Webplattform lassen sich in den MDN web docs nachlesen.

Der Beitrag Mozilla veröffentlicht Firefox 156 erschien zuerst auf soeren-hentzschel.at.