Best practice for temporarily disabling new logins during maintenance?

Hello, members.

What I would like to achieve

I would like to display a maintenance page before starting scheduled maintenance.
Before the actual maintenance begins, I would like to modify the login page so that new users cannot log in.

For example, I would like to display a message such as:
“Scheduled maintenance will begin in 9 hours.”

At the same time, I would like users who are already logged in to continue using their existing notebook servers without interruption.

We are exposing JupyterHub through the proxy-public LoadBalancer service and are not using an Ingress controller.

What I have tried

I found that I can disable the login button by overriding the login template as shown below.

Although users can still log in by directly accessing the authentication URL, I believe very few users would do that,
so simply disabling the login button is sufficient for our use case.

I apply the configuration using:

helm upgrade \
    --install jhub jupyterhub/jupyterhub \
    --namespace jhub \
    --create-namespace \
    --values config.yaml \
    --timeout 20m

Questions

  1. Is there a better or more standard way to implement a temporary maintenance mode in Zero to JupyterHub?
  2. Updating the login template requires running helm upgrade, which causes the Hub pod to restart. Is there any way to temporarily change the login page without restarting the Hub pod?

Environment

  • CHART VERSION: 4.4.0
  • App Version: 5.5.0

Configuration

hub:
  extraFiles:
    login-template:
      mountPath: /usr/local/share/jupyterhub/custom_templates/login.html
      stringData: |
        {% extends "page.html" %}

        {% if announcement_login is string %}
          {% set announcement = announcement_login %}
        {% endif %}

        {% block login_widget %}
        {% endblock login_widget %}

        {% block main %}
          <div id="login-main" class="container">
            <div class="service-login">
              <h1>Maintenance</h1>
              <p>JupyterHub is currently under maintenance.</p>
              <p>New logins are temporarily disabled. Please try again later.</p>

              <button role="button"
                      class="btn btn-jupyter btn-lg"
                      disabled>
                Sign in with {{ login_service }}
              </button>
            </div>
          </div>
        {% endblock main %}

  extraConfig:
    # Maintenance
    templates: |
      c.JupyterHub.template_paths = ["/usr/local/share/jupyterhub/custom_templates"]


proxy:
  https:
    enabled: true
    type: manual
    manual:
      cert:
      key:

Best regards,

I don’t know if this can be considered as “best practice”, but I would overwrite the JupyterHub authenticator with a custom authenticate method (see Authenticators — JupyterHub documentation).

For example:

class CustomAuthenticator(Authenticator): # (e.g., GenericOAuthenticator)

    def authenticate(self, data, handler):
        # Fetch maintenance status from database
        maintance = ...

        if maintenance:
            return None
        
         # Delegate login to your original authenticator
         return super().authenticate(data, handler)

This way, you can change the database to modify the login behavior without the need of restarting the hub. Furthermore, you can even fetch the maintance status from the database and pass it as a variable to the JupyterHub template to provide a dynamic maintance login page.

@Paul2708 Thank you.

It looks like I need to investigate how to store parameters in a database, but the approach you suggested seems promising, so I would like to try implementing it.

You dont need to do anything fancy with the DB. You can simply touch a file in the folder where JupyterHub has read access and in the authenticate method you can check for the presence of that file. At the end of the maintenance you can delete that file.