Maintenance page workaround - can this be done?

i use the dual container build behind Cloudlfare CDN. the dual container minimizes downtime and my two main production forums both have 30 seconds max offline during rebuilds. i like having a maintenance page because the default web server is down page is ugly and gives no clue to how long it will be offline.

for example, for a site at your-domain.com i simply use a Cloudflare workers route set to *yourdomain.com/* (include the astricks).

Step 1: Create the worker page

you can setup the maintenance page at the workers & pages settings page on Cloudflare - click the create application button:

then use the hello world template:

then give it a name and click deploy:

then go to the overview page for that maintenance page and click edit code:

and paste this code into the worker.js code window (substitute your domain and whatever message you want, edit text, the color of text, background, etc):

export default {
  async fetch(request, env, ctx) {
    try {
      // Fetch the original request from your server
      const response = await fetch(request);

      // If YOUR SITE is swapping containers, it throws a 502, 521, or 530
      if (response.status === 502 || response.status === 521 || response.status === 530) {
        return returnCustomErrorPage();
      }

      // If everything is fine, return the normal forum traffic
      return response;
    } catch (e) {
      // If the server is completely unreachable
      return returnCustomErrorPage();
    }
  }
};

function returnCustomErrorPage() {
  const html = `
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>System Refresh - YOUR-SITE.com</title>
      <style>
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; text-align: center; padding: 50px; color: #333; background-color: #f9f9f9; }
        h1 { font-size: 2.5em; margin-bottom: 0.5em; color: #9400D3; }
        p { font-size: 1.2em; line-height: 1.5; }
        .container { max-width: 600px; margin: 0 auto; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
      </style>
    </head>
    <body>
      <div class="container">
        <h1>Just a quick update window</h1>
        <p><strong>YOUR-SITE</strong> is undergoing a brief 30-second system refresh.</p>
        <p>Grab a quick sip of your drink—this page will automatically refresh as soon as we are back online.</p>
      </div>
      <script>
        // Automatically check if the site is back up every 10 seconds
        setInterval(function() {
          window.location.reload();
        }, 10000);
      </script>
    </body>
    </html>
  `;

  return new Response(html, {
    status: 503, // 503 is best for SEO so Google doesn't penalize you for downtime
    headers: {
      "Content-Type": "text/html;charset=UTF-8",
      "Retry-After": "30"
    }
  });
}

it should look something like this. click deploy to save it:

Step 2: Add the route

now go to the Cloudflare worker route page and click the add route button to pull up the new route modal, fill in the domain route and select the worker page you just made, then click save:

Step 3: Running updates or rebuilds

now, when you ssh into your server and run your system updates or do a rebuild, instead of a website down or error page, this page will show when the downtime actually happens (i use 30 seconds in mine because that is the max for my sites)

i tend to add and delete my workers route page whenever i am doing maintenance, but some like to just leave it there all the time (i leave the actual code for the page though, i just add and remove the route).

i think that’s it.

Optional: Run updates with a script and schedule

i also use a unix shell script on my server to run the dual container specific update, which i created like this:

cat << 'EOF' > /root/update-web.sh
#!/bin/bash
cd /var/discourse
echo "➡️ Pulling latest Discourse docker scripts..."
git pull

echo "➡️ Bootstrapping new web container in the background (takes ~8 mins)..."
./launcher bootstrap web_only

if [ $? -eq 0 ]; then
    echo "✅ Bootstrap successful! Swapping containers..."
    ./launcher destroy web_only && ./launcher start web_only
    echo "🚀 Done! Site updated with almost zero downtime."
else
    echo "❌ Bootstrap failed! Aborting swap to keep current site online."
fi
EOF

chmod +x /root/update-web.sh

then i run it with at the root prompt on my server with the command ./update-web.sh.


:warning: edit: don’t do the steps below unless you have a paid cloudflare account.

you can automate it to a specific time, such as 1am Sunday morning your local time with a cron job. for me 1:00am local is 8:00 AM UTC, (you can figure out the UTC date of your server by typing date at the command prompt when ssh’d in).

so:

run this command to open your server’s task scheduler:

crontab -e

(if it asks you to choose an editor, select your preferred editor, 1 for nano is probably easiest)

i scroll to the bottom of the comments and paste this:

0 8 * * 0 /root/update-web.sh >> /var/log/discourse-update.log 2>&1

(which means run the /root/update-web.sh file at minute 0, hour 8 (UTC), every sunday morning)

then save and exit (if you are using nano):

  1. Ctrl + O to save.
  2. Enter to confirm.
  3. Ctrl + X to exit.

then i can run cat /var/log/discourse-update.log when i wake up on sunday mornings to check if it ran properly. if you do use a cron task scheduler, then you will want to leave the workers page route.

i think that is all. let me know if you have questions lol. it is much simpler than i made it seem lol. :grin: