How to create a systemd service for Jupyter Notebook

A Jupyter Notebook server started from an interactive terminal stops when that shell closes. A systemd service ties the Notebook process to the Linux service manager, which fits a shared workstation, lab server, or remote host that should keep serving notebooks after logout.

Notebook 7 runs on Jupyter Server behind the Notebook interface, so the service can still call jupyter notebook while using ServerApp options for the listener, browser behavior, and notebook root directory. A dedicated service account separates Notebook files and runtime state from an administrator's login account.

The unit below binds Notebook to 127.0.0.1 on port 8899 and disables automatic browser launch. Use a password, HTTPS, an SSH tunnel, or another access-control layer before binding Notebook to a network address, and treat tokenized URLs in service logs as secrets.

Steps to create a Jupyter Notebook systemd service:

  1. Create a dedicated service account for Notebook.
    $ sudo useradd --system --create-home --home-dir /home/jupyter --shell /usr/sbin/nologin jupyter

    Skip this command if the account already exists. Use the same account name in the unit file so Notebook reads the intended home directory and Jupyter runtime files.

  2. Create the notebook directory for the service.
    $ sudo install --directory --owner=jupyter --group=jupyter --mode=0750 /srv/notebooks

    ServerApp.root_dir will point Notebook at this directory, so place shared notebooks somewhere the jupyter service account can read and write.

  3. Confirm the service account can run the installed Notebook command.
    $ sudo -u jupyter /opt/jupyter/venv/bin/jupyter notebook --version
    7.6.0

    Replace /opt/jupyter/venv/bin/jupyter with the jupyter path from your installation, such as a virtual environment under /home/jupyter.
    Related: How to install Jupyter Notebook on Ubuntu

  4. Open the systemd unit file.
    $ sudoedit /etc/systemd/system/jupyter-notebook.service
  5. Add the Notebook service unit.
    [Unit]
    Description=Jupyter Notebook
    After=network.target
    
    [Service]
    Type=simple
    User=jupyter
    Group=jupyter
    WorkingDirectory=/srv/notebooks
    Environment="PATH=/opt/jupyter/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ExecStart=/opt/jupyter/venv/bin/jupyter notebook --no-browser --ip=127.0.0.1 --port=8899 --ServerApp.root_dir=/srv/notebooks
    Restart=on-failure
    RestartSec=10
    
    [Install]
    WantedBy=multi-user.target

    Do not change --ip=127.0.0.1 to a public address until authentication, transport security, and firewall rules are ready. A tokenized startup URL grants access to the running Notebook session.

    WorkingDirectory sets the process directory, while ServerApp.root_dir sets the Notebook file browser root.
    Tool: systemd Unit Generator

  6. Verify the unit file syntax.
    $ sudo systemd-analyze verify /etc/systemd/system/jupyter-notebook.service

    No output means systemd parsed the unit file successfully.

  7. Reload systemd after saving the unit.
    $ sudo systemctl daemon-reload
  8. Enable and start the Notebook service.
    $ sudo systemctl enable --now jupyter-notebook
    Created symlink /etc/systemd/system/multi-user.target.wants/jupyter-notebook.service -> /etc/systemd/system/jupyter-notebook.service.
  9. Check the service state.
    $ sudo systemctl is-active jupyter-notebook
    active
  10. List the running Jupyter server for the service account.
    $ sudo -u jupyter /opt/jupyter/venv/bin/jupyter server list
    Currently running servers:
    http://127.0.0.1:8899/?token=<token> :: /srv/notebooks

    Mask real tokens before sharing terminal output, screenshots, or tickets.

  11. Check the Notebook login page on the configured listener.
    $ curl --include --silent --show-error http://127.0.0.1:8899/login
    HTTP/1.1 200 OK
    Server: TornadoServer/6.5.7
    Content-Type: text/html; charset=UTF-8
    X-Content-Type-Options: nosniff
    Content-Security-Policy: frame-ancestors 'self'; report-uri /api/security/csp-report
    ##### snipped #####

    A 200 OK response from /login proves the managed Notebook process is accepting local HTTP requests. Use an SSH tunnel, reverse proxy, or HTTPS endpoint for remote browser access instead of exposing the local listener directly.