# Maintenance mode
URL: https://synquic.tech/docs/guides/maintenance-mode.md

Show visitors a "down for maintenance" page on every domain of a project, instantly and without a redeploy, while the app keeps running behind it.

import { Step, Steps } from 'fumadocs-ui/components/steps';

Sometimes you need visitors to stay away for a while: a database migration, a data import, a big upgrade.
**Maintenance mode** puts a notice in front of the app. While it's on, the edge answers every domain of the
project with a maintenance page instead of passing requests to the app.

It takes effect in seconds, needs no redeploy, and doesn't stop anything: the app, its services and its
database keep running behind the page, so you can keep working on them.

<Callout title="What you need first">
- A project on one of your own servers. The page is served by the server's edge proxy, so it covers the
  project's domains on that server.
- Permission to change the project's settings.
</Callout>

## Turn it on

<Steps>

<Step>

### Open Maintenance

Open the project and choose **Maintenance** in the project menu. You see the **Maintenance mode** card.

</Step>

<Step>

### Optionally write a message

Type a short note in **Message**, for example `We're upgrading the database. Back by 18:00 UTC.` It is plain
text (up to 1,000 characters) shown on the built-in page. Press **Save page** to keep it.

</Step>

<Step>

### Switch it on

Turn on the **Maintenance mode** switch. The toast says **Maintenance mode is on**, and the hint under the
switch reads **On: visitors see the maintenance page.** That's it: open one of the project's domains and you
see the page.

</Step>

</Steps>

To end maintenance, switch it off. The toast says **Maintenance mode is off** and the app is served normally
again, straight away.

## What visitors get

- The built-in page, titled **Down for maintenance**, with your message under it.
- HTTP status **503 Service Unavailable** with `Retry-After: 300`. Browsers, search engines and uptime monitors
  read that as "temporarily unavailable, come back later", so your search ranking isn't affected by a short
  window.
- `Cache-Control: no-store`, so nobody keeps a cached copy of the notice after you switch it off.
- Every path gets the page, including API calls, path routes such as `/api/` and asset URLs, on every domain
  of the project. A domain set up only to redirect to another one keeps redirecting, and a project that has
  never been deployed keeps showing its "not deployed yet" placeholder instead.

## Use your own page

Press **Use a custom HTML page instead** and paste a complete HTML document into **Custom HTML** (up to
32 KB). Press **Save page**. While it's filled in, Synq serves your page instead of the built-in one, with
the same 503 status. Leave it empty to go back to the built-in page.

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Back soon</title>
    <style>body{font-family:system-ui;text-align:center;padding:4rem}</style>
  </head>
  <body>
    <h1>We'll be right back</h1>
    <p>Scheduled maintenance until 18:00 UTC. Follow status.example.com for updates.</p>
  </body>
</html>
```

Keep the page self-contained: inline the CSS, and load images from another domain or embed them as `data:`
URLs, since requests to the project's own domain get the maintenance page too.

## What keeps working

- **The app and its services** keep running. They can still talk to each other on the project's private
  network, and cron-style work inside them continues.
- **Published ports** (a database's **Public access**, game or mail ports) aren't behind the edge, so they
  stay reachable.
- **Certificates** keep renewing, and the panel, API and webhooks are served on Synq's own domain, not the
  project's.
- **Deploys.** You can deploy while maintenance is on; visitors keep seeing the page until you switch it
  off. That's the usual pattern: switch on, deploy or migrate, check, switch off.

To look at the real app while visitors see the notice, reach it without its domain: publish a
**Localhost** port and use an SSH tunnel (see [Public ports](/docs/guides/ports#public-or-localhost)).

## From the API

```bash
# Turn on with a message
curl -X PATCH -H "Authorization: Bearer $SYNQ_TOKEN" -H "Content-Type: application/json" \
  -d '{"maintenance":{"enabled":true,"message":"Upgrading the database, back by 18:00 UTC."}}' \
  "$SYNQ_URL/api/projects/$PROJECT_ID"

# Turn off, keeping the message for next time
curl -X PATCH -H "Authorization: Bearer $SYNQ_TOKEN" -H "Content-Type: application/json" \
  -d '{"maintenance":{"enabled":false,"message":"Upgrading the database, back by 18:00 UTC."}}' \
  "$SYNQ_URL/api/projects/$PROJECT_ID"
```

The body also accepts `html` for a custom page. The `maintenance` object replaces the saved one as a whole, so
send the `message` and `html` you want to keep with `"enabled":false` (that is what the panel's switch does).
`{"maintenance":{"enabled":false}}` also turns it off but drops a message or page you didn't send, and
`{"maintenance":null}` turns it off and clears the saved message and custom page.

`SYNQ_URL` is your panel address (for example `https://app.example.com`).

## Related guides

<Cards>
  <Card title="Custom domains" href="/docs/guides/custom-domains" description="The domains the maintenance page covers." />
  <Card title="Backups & restore" href="/docs/guides/backups-restore" description="Take a backup before the maintenance work starts." />
  <Card title="Rollback & redeploy" href="/docs/guides/rollback-redeploy" description="Go back to the previous release if the upgrade goes wrong." />
</Cards>
