Boot into Btrfs snapshots from GRUB on Fedora (grub-btrfs + snapper)

Published 11 July 2026

date
updated
env
linux Fedora 44 — Btrfs-on-LUKS root, SEPARATE ext4 /boot, UEFI, snapper already configured macos N/A

Plain snapper lets you recover while the system still boots: you log in, inspect the damage, revert files, or replace the root subvolume. It cannot help when an update breaks booting itself — a bad kernel, a broken initramfs, a botched upgrade that leaves you at a black screen. You can’t run snapper because you can’t get to a working system.

grub-btrfs closes that gap. It injects a “Fedora Linux snapshots” submenu into the GRUB boot screen, one entry per snapper snapshot. You pick a snapshot at boot and the system starts directly into that older state of / (read-only), entirely from the bootloader — so it works even when the normal system won’t. From there you can confirm the fix and make the rollback permanent.

This is the optional follow-on to Snapper restore points on Fedora (Btrfs). Do that setup first.

Optional / third-party. grub-btrfs is not in Fedora’s repos and touches the bootloader, and it interacts awkwardly with this machine’s separate ext4 /boot (see the caveat section). Read the caveats before deciding to install it. Steps 1 to 5 were applied and verified on this machine on the nested /.snapshots layout this how-to was originally written against — including the .snapshots.mount fix in Step 3, which Step 3 now explains when to skip. Re-verified end to end on 2026-08-14 on a fresh Fedora 44 install using the top-level /.snapshots layout (case A), where the packaged unit works as shipped and no override is needed.

Read this first — the separate-/boot caveat

On a default Fedora install (this machine included) /boot is its own ext4 partition, not part of the Btrfs root. Confirm with:

Terminal window
findmnt /boot

If /boot shows FSTYPE ext4 (or is the EFI System Partition), then:

  • The kernel image and initramfs are NOT captured in snapshots. Snapshots only cover the Btrfs / subvolume. /boot is shared across all snapshots.
  • Kernel modules (/usr/lib/modules/…) ARE in the snapshot (they live under /). So a snapshot can contain modules for a kernel version whose image no longer exists in /boot, or vice-versa — a version mismatch when you roll back across a kernel update.
  • What still works well: rolling back userspace and configuration — a bad dnf upgrade of packages, a broken config in /etc, a regression in an app. This is the common case and grub-btrfs handles it fine.
  • What is unreliable: rolling back across a kernel change using a snapshot entry, because the snapshot’s /boot is empty (it’s just a mountpoint) and the running kernel/initramfs come from the shared ext4 partition.
  • Good news for kernel problems specifically: Fedora keeps the last 3 kernels in /boot (installonly_limit=3), and GRUB’s normal “Advanced options for Fedora Linux” submenu already lets you boot an older kernel. For “the new kernel won’t boot,” that built-in menu is usually the better tool than a snapshot.

Bottom line: grub-btrfs on this layout is best understood as “boot an old root filesystem state to recover userspace,” not “a full point-in-time machine restore including the kernel.” If you want kernel-inclusive rollbacks, that requires putting /boot inside the Btrfs subvolume — a reinstall-level change, out of scope here.

Prerequisites

  • snapper already installed and configured with a root config and a populated /.snapshots (see the snapper how-to). Verify:
    Terminal window
    sudo snapper -c root list
    ls /.snapshots
  • Btrfs root filesystem. /boot may be separate (see caveat above).
  • GRUB as the bootloader with its config at /boot/grub2/grub.cfg (Fedora default on both UEFI and BIOS since Fedora 34 — the EFI stub at /boot/efi/EFI/fedora/grub.cfg just chainloads it). Confirm:
    Terminal window
    ls -l /boot/grub2/grub.cfg
  • sudo / root privileges.
  • Willingness to run a third-party COPR that modifies bootloader config.

Step 1 — Install grub-btrfs from the kylegospo COPR

grub-btrfs is not in Fedora’s repositories. The kylegospo fork is the well-maintained one patched to work with Fedora’s BLS (BootLoaderSpec) boot entries — which is why you do not need to disable BLS in /etc/default/grub with this fork.

Terminal window
sudo dnf copr enable -y kylegospo/grub-btrfs
sudo dnf install -y grub-btrfs

It does not pull in inotify-tools, and does not need to. Most grub-btrfs guides say the install brings in inotify-tools for the watcher — that is true of the upstream grub-btrfsd daemon, which shells out to inotifywait. The kylegospo package ships a systemd .path unit instead, and a path unit uses systemd’s own inotify. Verified on 2026-08-14: rpm -q --requires grub-btrfs lists no inotify dependency, the package is not installed, and the watcher works. If a guide tells you to install it, you are reading about the other packaging.

Step 2 — Confirm the Fedora paths in grub-btrfs config

The COPR package ships Fedora-correct defaults, but verify /etc/default/grub-btrfs/config contains (uncommented) the Fedora values:

Terminal window
grep -E 'MKCONFIG|GRUB_DIRNAME|SCRIPT_CHECK' /etc/default/grub-btrfs/config

Expected on Fedora:

GRUB_BTRFS_MKCONFIG=/usr/bin/grub2-mkconfig
GRUB_BTRFS_GRUB_DIRNAME="/boot/grub2"
GRUB_BTRFS_SCRIPT_CHECK=grub2-script-check
GRUB_BTRFS_MKCONFIG_LIB=/usr/share/grub/grub-mkconfig_lib

/usr/bin/grub2-mkconfig and /usr/sbin/grub2-mkconfig are the same file — Fedora is usrmerged and /usr/sbin is a symlink to bin, so either value works. The package ships the /usr/bin form.

If any are missing or point at Debian-style paths (/usr/sbin/grub-mkconfig, /boot/grub), edit the file and correct them:

Terminal window
sudoedit /etc/default/grub-btrfs/config

Other useful knobs in that file: GRUB_BTRFS_LIMIT (max snapshot entries to list, default 50), GRUB_BTRFS_TITLE_FORMAT (what each entry shows — date, type, description), and GRUB_BTRFS_SHOW_SNAPSHOTS_FOUND.

Step 3 — Enable the watcher (with the Fedora .snapshots.mount fix)

A systemd unit watches /.snapshots and rebuilds the GRUB snapshot submenu whenever snapper creates or deletes a snapshot, so the boot menu always matches your current snapshots. Check what the package shipped:

Terminal window
systemctl list-unit-files 'grub-btrfs*'

The current kylegospo COPR ships a path unit plus a static service it triggers:

grub-btrfs.path disabled ← this is the one you enable
grub-btrfs.service static ← triggered by the path unit; do NOT enable directly

The Fedora gotcha — and whether it applies to you

The packaged unit hard-binds to a .snapshots.mount systemd unit:

[Unit]
Requires=\x2esnapshots.mount
After=\x2esnapshots.mount
BindsTo=\x2esnapshots.mount
[Install]
WantedBy=\x2esnapshots.mount

systemd only generates that unit when /.snapshots is a separately mounted filesystem — an fstab entry, or an explicit mount. Whether it exists therefore depends on how /.snapshots was set up, and that is exactly the choice made in Step 1b of the snapper how-to. Check which case you are in before enabling anything:

Terminal window
findmnt /.snapshots
systemd-escape -p --suffix=mount /.snapshots # the unit name systemd would use
systemctl list-units --all --type=mount | grep -i snapshots

Case A — /.snapshots is a top-level subvolume mounted from fstab (the layout recommended by the snapper how-to). findmnt prints a line, and systemctl shows the mount unit under its escaped name — verified on this machine:

\x2esnapshots.mount loaded active mounted /.snapshots

That is precisely the name the packaged unit references. Nothing to fix: skip to the enable step below, and skip the override entirely. The binding is a feature here — the watcher starts and stops with the mount it watches.

Case B — /.snapshots is a nested subvolume inside / (what snapper create-config produces if left alone). findmnt prints nothing, no mount unit exists, and enabling the path unit fails:

Terminal window
$ sudo systemctl enable --now grub-btrfs.path
Unit /usr/lib/systemd/system/grub-btrfs.path is added as a dependency to a non-existent unit .snapshots.mount.
Failed to start grub-btrfs.path: Unit .snapshots.mount not found.

Apply the override below.

The fix for case B — a local override

Replace the unit with one that drops the mount dependency and attaches the watcher to a normal target. A file at /etc/systemd/system/grub-btrfs.path fully overrides the packaged one at /usr/lib/systemd/system/ and survives package updates. Its contents:

# Local override of /usr/lib/systemd/system/grub-btrfs.path
# The packaged unit binds to a `.snapshots.mount` unit that only exists when
# /.snapshots is separately mounted (openSUSE-style). On Fedora's nested-subvol
# layout that unit doesn't exist, so drop the dependency and use a real target.
[Unit]
Description=Monitors for new snapshots
[Path]
PathModified=/.snapshots
Unit=grub-btrfs.service
[Install]
WantedBy=multi-user.target

Paste tip: don’t paste this as a sudo tee <<'EOF' here-doc if your shell mangles indented multi-line input. Write it to a normal file and copy it in with a single line: sudo install -D -m 0644 <file> /etc/systemd/system/grub-btrfs.path (or use sudoedit /etc/systemd/system/grub-btrfs.path).

Enable the watcher (both cases)

Terminal window
sudo systemctl daemon-reload
sudo systemctl enable --now grub-btrfs.path
systemctl status grub-btrfs.path --no-pager

Expect Active: active (waiting) and Triggers: ● grub-btrfs.service. The Loaded: line tells you which unit is in force — /etc/systemd/system/grub-btrfs.path in case B, /usr/lib/systemd/system/grub-btrfs.path in case A. You do not need to change the watch path — /.snapshots is snapper’s default in both layouts.

Step 4 — Generate the snapshot menu for the first time

Trigger the snapshot menu generator, then rebuild the main GRUB config:

Terminal window
sudo /etc/grub.d/41_snapshots-btrfs
sudo grub2-mkconfig -o /boot/grub2/grub.cfg

Watch the output for a line like Found snapshot … per snapper snapshot and Found N snapshot(s). If it finds zero, snapper hasn’t populated /.snapshots yet — create one (sudo snapper -c root create -d test) and rerun.

Gotcha — run these from a directory root can enter

Both commands fail if your shell’s current directory is one that root cannot access. The symptom is easy to misread, because it mostly looks like nothing happened:

$ sudo /etc/grub.d/41_snapshots-btrfs && sudo grub2-mkconfig -o /boot/grub2/grub.cfg
$

No output at all from the generator (zero bytes on both stdout and stderr, exit status 1), and from grub2-mkconfig only:

/usr/bin/grub2-probe: error: cannot restore the original directory.

grub2-probe saves its working directory and restores it afterwards. Under sudo the working directory is inherited from your shell, so if root cannot chdir() back into it, the tool aborts before doing any work. The classic trigger is a FUSE mount owned by your user and mounted without allow_other — root is not allowed in, by design. On this machine that is the rclone Dropbox mount from Mount Dropbox on Fedora with rclone: running the step from anywhere under ~/Dropbox breaks it.

What makes it nasty is that it fails silently and without side effects: /boot/grub2/grub-btrfs.cfg is never created and grub.cfg keeps its old timestamp, so nothing looks damaged — you simply get no snapshot submenu, with no error to search for.

Fix: run from a directory root can enter. A subshell keeps your own working directory unchanged:

Terminal window
(cd / && sudo /etc/grub.d/41_snapshots-btrfs && sudo grub2-mkconfig -o /boot/grub2/grub.cfg)

Confirm it actually wrote something — this is the check worth doing, since success and silent failure look alike:

Terminal window
sudo ls -l /boot/grub2/grub-btrfs.cfg

This applies to every sudo command in this how-to, not just Step 4, and to grub2-mkconfig generally — including the manual rebuild below and the removal step.

Step 5 — Reboot and verify

Terminal window
sudo systemctl reboot

At the GRUB screen you should now see an extra entry:

Fedora Linux
Advanced options for Fedora Linux
Fedora Linux snapshots ← new
└─ 2026-07-11 18:43 pre dnf install
└─ 2026-07-11 12:00 single timeline
└─ ...

Open it, pick a snapshot, and the system boots into that state read-only (a safety measure — you’re inspecting, not yet committing). You are now running the old root filesystem.

Recovering: make a rollback permanent

Do not use snapper rollback for this on Fedora. It is the command every guide reaches for, and on a stock Fedora install it is silently ignored: it only repoints the filesystem’s default subvolume, while both /etc/fstab and the kernel command line (rootflags=subvol=root) pin the root subvolume by name. You reboot into the unchanged system, with no error to tell you why. The full explanation and the procedure that works are in the snapper how-to: Rolling back the whole system. The short version is below.

Booting a snapshot from the GRUB menu is temporary — it changes rootflags= for that one boot only. The normal menu entry still points at subvol=root, so the next ordinary boot lands back in the broken system. Making the recovery permanent means putting the snapshot’s content back under the name root.

You have two paths depending on whether the system still boots normally.

A) System still boots normally — you don’t need grub-btrfs at all. Prefer the least destructive option that fixes the problem:

  1. Find what changed, using the pre/post pair from the transaction that broke things:
    Terminal window
    sudo snapper -c root list
    sudo snapper -c root status <pre>..<post>
  2. If it is a handful of files, revert just those — no reboot, no rollback:
    Terminal window
    sudo snapper -c root undochange <pre>..<post>
  3. Only if the system is too broken to repair file by file, swap the root subvolume as described in the snapper how-to, then reboot.

B) System won’t boot — this is what grub-btrfs is for:

  1. At the GRUB menu, open Fedora Linux snapshots and boot a known-good snapshot (read-only).
  2. Confirm it’s actually the state you want (log in, check the thing that broke is fine). Note which snapshot number you are running:
    Terminal window
    findmnt -no OPTIONS / # → subvol=/.snapshots/<N>/snapshot
  3. Make it permanent by swapping the subvolume. The booted snapshot is read-only, but that does not matter — you are writing to the filesystem, not to the snapshot:
    Terminal window
    sudo mkdir -p /mnt/btrfs-top
    sudo mount -o subvolid=5 "$(findmnt -no SOURCE / | sed 's/\[.*//')" /mnt/btrfs-top
    sudo mv /mnt/btrfs-top/root /mnt/btrfs-top/root.broken
    sudo btrfs subvolume snapshot /mnt/btrfs-top/.snapshots/<N>/snapshot /mnt/btrfs-top/root
  4. Reboot into the restored, writable system — pick the normal Fedora entry this time, not a snapshot entry:
    Terminal window
    sudo systemctl reboot
  5. Once satisfied, reclaim the space (root.broken contains nested subvolumes; remove those first). See the snapper how-to for the details and for keeping root.broken as a way back.

If your /.snapshots is a top-level subvolume (Step 1b of the snapper how-to), your snapshot history survives this swap untouched, including the state you just rolled away from. If it is nested inside root, the history goes with root.broken — do not delete that subvolume until you have what you need out of it.

On this machine’s separate /boot, if the failure was a kernel/initramfs problem rather than a userspace one, prefer GRUB’s “Advanced options for Fedora Linux” to boot a previous kernel (see the caveat section) — a root-filesystem snapshot won’t swap the kernel.

Keeping the menu fresh

With the watcher from Step 3 enabled, the snapshot submenu regenerates automatically as snapper adds/removes snapshots — including the pre/post pairs your dnf hook creates. To rebuild it by hand at any time:

Terminal window
(cd / && sudo /etc/grub.d/41_snapshots-btrfs && sudo grub2-mkconfig -o /boot/grub2/grub.cfg)

The cd / is not cosmetic — see the working-directory gotcha in Step 4.

Optional — hide the GRUB menu until something goes wrong

Once the snapshot submenu exists, the boot menu sits on screen for GRUB_TIMEOUT seconds at every boot, while you only ever need it when something is broken. Fedora ships a mechanism that hides it on healthy boots and brings it back by itself after a failed one — which is precisely the moment the snapshot entries matter.

This is not GRUB_TIMEOUT=0, which hides the menu unconditionally, including when you need it. It is a variable in GRUB’s environment block:

Terminal window
sudo grub2-editenv - set menu_auto_hide=1

No grub2-mkconfig is required: the logic is already in your generated grub.cfg, contributed by three snippets grub2-tools installs by default.

SnippetRole
/etc/grub.d/10_reset_boot_successsets menu_hide_ok=1 if the previous boot was flagged successful, then resets boot_success=0 for the current boot
/etc/grub.d/12_menu_auto_hidewhen menu_auto_hide=1 and menu_hide_ok=1 → timeout_style=hidden, timeout=1
/etc/grub.d/14_menu_show_oncedisplays the menu once on request, then clears the request

Confirm they made it into your config — verified present on Fedora 44 with grub2-tools-2.12-60.fc44:

Terminal window
sudo sh -c 'grep -c menu_auto_hide /boot/grub2/grub.cfg; grub2-editenv - list'

A non-zero count plus menu_auto_hide=1 in the listing is all you need. The setting takes effect at the next boot.

What counts as a “failed boot”

boot_success is set to 1 from userspace by grub-boot-success.timer, a user timer that fires 2 minutes after your graphical session starts. It costs nothing at boot time — it writes a flag while you work.

Previous bootgrubenv at next bootWhat you see
Session ran ≥ 2 minutesboot_success=1no menu — a 1-second hidden window where a keypress (ESC) reveals it
Kernel panic, broken initramfs, black screen, session never openedboot_success=0the full menu with your GRUB_TIMEOUT, snapshot submenu included
You rebooted within 2 minutes of logging inboot_success=0the full menu — a harmless false positive
Offline update (system-update.target)boot_indeterminate incremented by grub-boot-indeterminate.servicehidden once, then treated as failed if that boot doesn’t succeed either

The two failure modes that send you looking for a snapshot — it doesn’t boot at all, and it boots but the desktop is unusable so you reboot immediately — both land on boot_success=0. You get the menu without doing anything.

Asking for the menu on purpose

The case auto-hide does not cover: the system boots perfectly, and three days later you decide to roll back. boot_success=1, so the menu is hidden and you are left spamming ESC at a 1-second window. From a system that still boots, request it explicitly instead:

Terminal window
sudo grub2-editenv - set menu_show_once_timeout=10

The next boot shows the menu for 10 seconds, then the variable clears itself. menu_show_once=1 does the same with a fixed 60-second timeout — the menu is interactive, so pressing Enter boots immediately; the timeout only elapses if you walk away.

Caveats

  • fastboot=1 removes the escape hatch. If that variable is set in grubenv, 12_menu_auto_hide uses timeout_style=menu with timeout=0 instead, which skips the keypress check entirely — the menu becomes unreachable from the keyboard. Check for it with grub2-editenv - list before relying on ESC.
  • GRUB must be able to write grubenv. The whole mechanism rests on GRUB resetting boot_success=0 at each boot. On this machine /boot is a separate ext4 partition, so save_env works normally — the one place where the separate-/boot layout from the caveat section is an advantage rather than a limitation. GRUB has no write support for Btrfs; Fedora patches around it with the env_block raw-offset variable you can see referenced in those snippets. If your /boot lives on Btrfs, verify the flag actually toggles (below) instead of assuming it does.
  • grubenv is not grub.cfg. The regenerations grub-btrfs triggers on every new snapshot rewrite grub.cfg and grub-btrfs.cfg, and leave grubenv alone. Your setting survives them.

Verify it, and reverting

The flag is observable from the running system. Right after logging in (under 2 minutes) it should read boot_success=0; a few minutes later, boot_success=1:

Terminal window
sudo grub2-editenv - list

To go back to a permanently visible menu:

Terminal window
sudo grub2-editenv - unset menu_auto_hide

Undoing this setup

grub-btrfs is non-destructive and easy to remove:

Terminal window
sudo systemctl disable --now grub-btrfs.path
sudo rm -f /etc/systemd/system/grub-btrfs.path # remove the local override
sudo systemctl daemon-reload
sudo dnf remove -y grub-btrfs
sudo grub2-mkconfig -o /boot/grub2/grub.cfg # regenerate a clean menu without snapshot entries

Your snapshots and snapper config are untouched — you only remove the boot-menu integration.

Notes and good practices

  • Third-party component. The kylegospo COPR is community-maintained, not Fedora-official. It can lag or break after a major GRUB / grub2-tooling update; re-run grub2-mkconfig and check the boot menu after such updates.
  • BLS stays enabled. With the kylegospo fork you keep Fedora’s default GRUB_ENABLE_BLSCFG=true — do not disable BLS (that’s only needed for the upstream Antynea version or openSUSE-style layouts).
  • Snapshots are still not a backup. They live on the same disk. grub-btrfs improves recovery convenience, not durability. Keep an off-disk backup.
  • Same recovery without grub-btrfs: boot a Fedora live USB, unlock LUKS, mount the Btrfs top level (subvolid=5), and swap the root subvolume there. grub-btrfs saves you the USB and lets you verify the snapshot by running it before committing to it — it does not unlock a recovery you couldn’t otherwise perform.
  • The watcher’s mount binding is layout-dependent. If you later move /.snapshots between the nested and top-level layouts, revisit Step 3: the packaged unit works in one case and fails in the other. A watcher that silently stopped starting leaves you with a stale snapshot menu, which is worse than no menu at all — it lists snapshots that may no longer exist.
  • Keep snapper descriptions ASCII. Snapshot descriptions become GRUB menu titles, and non-ASCII characters are HTML-escaped on the way in: a description written baseline — fresh snapper install shows up in the boot menu as baseline &#x2014; fresh snapper install. Purely cosmetic, but the boot menu is exactly where you don’t want to squint. Use a plain -.
  • Read-only first is intentional. Booting a snapshot read-only prevents you from accidentally writing to a snapshot you might not keep, and the boot itself is a one-off (rootflags= for that entry only). Nothing is committed until you swap the root subvolume.

Quick reference

ActionCommand
Check if /boot is separatefindmnt /boot
Enable COPR + installsudo dnf copr enable -y kylegospo/grub-btrfs && sudo dnf install -y grub-btrfs
Verify Fedora paths in configgrep -E 'MKCONFIG|GRUB_DIRNAME|SCRIPT_CHECK' /etc/default/grub-btrfs/config
See which watcher unit shippedsystemctl list-unit-files 'grub-btrfs*'
Which /.snapshots layout do I have?findmnt /.snapshots (a line = mounted, override not needed; no output = nested, override needed)
Install the .path override (nested layout only)sudo install -D -m 0644 <file> /etc/systemd/system/grub-btrfs.path
Enable watcher (path unit)sudo systemctl daemon-reload && sudo systemctl enable --now grub-btrfs.path
Live-test the watchersudo snapper -c root create -d test && sleep 3 && systemctl status grub-btrfs.service --no-pager
Regenerate snapshot menu(cd / && sudo /etc/grub.d/41_snapshots-btrfs && sudo grub2-mkconfig -o /boot/grub2/grub.cfg)
Nothing happened / no output at all?You ran it from a directory root can’t enter (e.g. a FUSE mount like ~/Dropbox). Re-run from / — see Step 4
Did the menu actually get written?sudo ls -l /boot/grub2/grub-btrfs.cfg
Which snapshot am I booted into?findmnt -no OPTIONS / → subvol=/.snapshots/<N>/snapshot
Revert a few files (preferred)sudo snapper -c root undochange <pre>..<post>
Make a rollback permanentswap the root subvolume — not snapper rollback, which is ignored on Fedora
Boot an older kernel insteadGRUB → “Advanced options for Fedora Linux”
Hide the menu until a boot failssudo grub2-editenv - set menu_auto_hide=1
Show the menu once (10 s) at next bootsudo grub2-editenv - set menu_show_once_timeout=10
Inspect GRUB’s saved statesudo grub2-editenv - list
Make the menu permanently visible againsudo grub2-editenv - unset menu_auto_hide
Remove grub-btrfssudo dnf remove -y grub-btrfs && sudo grub2-mkconfig -o /boot/grub2/grub.cfg

Files / services touched

Path or unitPurpose
/etc/default/grub-btrfs/configgrub-btrfs settings (mkconfig path, grub dir, entry limit/format)
/etc/grub.d/41_snapshots-btrfsGRUB generator script that emits the snapshot submenu
/boot/grub2/grub.cfgmain GRUB config; includes a stub that loads grub-btrfs.cfg
/boot/grub2/grub-btrfs.cfgthe generated snapshot submenu, refreshed by the watcher
/etc/systemd/system/grub-btrfs.pathlocal override dropping the .snapshots.mount dep — only on a nested /.snapshots layout (case B in Step 3)
grub-btrfs.path → grub-btrfs.servicepath watcher that regenerates grub-btrfs.cfg when snapshots change
/boot/grub2/grubenvGRUB environment block: saved_entry, boot_success, menu_auto_hide — untouched by menu regeneration
/etc/grub.d/10_reset_boot_success, 12_menu_auto_hide, 14_menu_show_onceFedora’s menu auto-hide logic, shipped by grub2-tools (optional section above)
grub-boot-success.timer (user unit)flags the boot successful 2 minutes into the graphical session