Xsession in Debian

Xsession is a default way of starting, well, nothing less but X sessions in Debian. You can read about it in detail by running man xsession, or read source code of the script itself: /etc/X11/Xsession. It’s not very complicated, but because it’s a shell script, it can be a little intimidating for newcomers. That’s why I’m publishing this small introduction.

Keep in mind that it’s based on Debian Stretch (9.0, stable as of writing this article), so things might be anywhere from “a little different” to “totally irrelevant” for other Debian releases.


Xsession is itself quite straightforward. It first sets some environment variables, does a little bookkeeping and then it sources all scripts from /etc/X11/Xsession.d, one by one, with help of run-parts. This is the place where a lot of recommendations for Debian X session management is implemented, like that a good place to auto start X applications is ~/.xsessionrc.

It is important to remember that all scripts in Xsession.d are sourced, not executed. This way all variables, functions and flags (global set +e) defined before are available for all consecutive scripts.

Some third-party packages may install their own scripts into Xsession.d (for example flatpak does that). They’re usually transparent to end-user, but sometimes they can be controlled one way or the other. Common way of adding simple, user-controllable flags is Xsession.options file. Keep in mind however that has_option function is defined by 20x11-common_process-args so if you want to process options with help of this function, you have to ensure that your script is sourced after common_process_args.

Other scripts in Xsession.d are shipped by default for all Debian installations with X capabilities. These are the important ones, because they define common environment variables and functions which are re-used in other scripts.


This is where $STARTUP variable is initialized. It’s quite important, because it names a script or program which will be executed later as a window/session manager.

Users can directly affect executed command by passing a single optional argument to Xsession and common_process-args will process it and assign to $STARTUP. This argument accepts also some special names, which are handled differently:

This script also defines has_option function, used for parsing of Xsession.options file.

30x11-common_xresources, 40x11-common_xsessionrc

These 2 scripts are sourced right after Xsession arguments are processed.

First one merges all system-wide X resources files found in /etc/X11/Xresources and user-specific one from ~/.Xresources.

Second simply sources ~/.xsessionrc file, so all user-defined environment variables found there will be globally available for the whole X session from now on and commands will be executed (note that commands should typically be run in a background to not block execution of Xsession).


This script simply uses xhost to give access to X server to the local user.


If Xsession wasn’t passed any argument, or if passed argument is not executable, $STARTUP won’t be set and common_determine-setup will run the default discovery mechanism of what should be run instead:

  1. ~/.xsession and ~/.Xsession are tried (because some users apparently like to start their file names with capital letters).
  2. default system-wide session manager: x-session-manager
  3. default system-wide window manager: x-window-manager
  4. default system-wide terminal emulator: x-terminal-emulator


This one often confuses a lot of people. Historically, there always was a problem with finding a good and convienient way to run SSH Agent and even today a lot of people run it from their .bashrc every time new shell starts. Debian solved it by binding its start with start of X session.

Anyway, if no previous ssh-agent appears to be running (e.g. gpg-agent with enabled support of OpenSSH Agent Protocol), $STARTUP will be wrapped with ssh-agent call and executed as agent’s subprocess.


Finally we execute our session manager, as common_start is typically the last file sourced by Xsession. It’s quite straightforward:


It replaces (via exec) the current shell with command stored in $STARTUP variable. This way executed command inherits the whole environment set up by all previous scripts.

When Xsession will be run

Typically Xsession is sourced (not executed) whenever /etc/X11/xinit/xinitrc is executed. This is handled by startx script (but not xinit, which is wrapped by startx and by itself will only load .xinitrc from user’s home directory).

Xsession is also sourced by display managers, but there are no clear rules when Xsession is used, when it isn’t, and what arguments are passed to it. Documentation of most Display Managers also lacks this knowledge so often all you’re left is the good old trial and error.

This is the source of a lot of headaches about files not being read (or being read unintentionally) during startup. The rule of thumb is: configure your system to ensure Xsession is executed and you’re back on a well-known waters.


Lightdm allows configuring it to use a session-wrapper, which is a script to run session with. This is pre-configured to be Xsession in /usr/share/lightdm/lightdm.conf.d/01_debian.conf.

Typically all available types of X sessions are read in a form of .desktop files from /usr/share/xsessions and some other directories. On login screen Lightdm only allows selecting one of those and passes the value of its Exec field to the session-wrapper as the first argument, which will cause executing correct program.

There is a special .desktop file there, used for so-called “default sessions”, which has Exec=default. It will be detected by 20x11-common_process-args and will result in running a fallback mechanism described in 50x11-common-determine-startup.


SDDM is a little more sane than Lightdm. It doesn’t have hidden settings or use any obscure features. For custom sessions it executes /etc/sddm/Xsession, which ultimately sources /etx/X11/Xsession itself.

SDDM tries to source .profile, .bash_profile, .zprofile etc. It’s a little unusual, because these files are usually boud to terminal sessions and graphical sessions usually source .xprofile.


GDM3 runs a bunch of scripts stored in different directories in /etc/gdm3, but ultimately one of them is /etc/gdm3/Xsession. Yep, it has a custom Xsession script, which is similar in some places to the default one, but also adds a bunch of its own logic. For example, if no argument is passed, it defaults to “failsafe” mode.

It doesn’t source the default Xsession, but sources scripts from /etc/X11/Xsession.d by itself. Before that it also tries to source profile and xprofile files.

It also aliases “default” session with a word “custom”.