Skip to content

Fix broken database backup / restore commands - #2156

Merged
marcleblanc2 merged 24 commits into
mainfrom
marc/fix-psql-on-error-stop
Nov 13, 2026
Merged

marcleblanc2 merged 24 commits into
mainfrom
marc/fix-psql-on-error-stop

Conversation

@marcleblanc2

@marcleblanc2 marcleblanc2 commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Problems

  • The database restore commands passed -v ERROR_ON_STOP=1 to psql, a typo for ON_ERROR_STOP, so restores hid their errors and exited 0

  • Fixing the typo exposed more broken steps

Solution

  • This PR fixes the errors
  • Merged the duplicate restore procedures on each page

Test results

  • Every step was run exactly as written, on both Docker Compose and a Helm 8.2.0 deployment
    • No errors
    • The restored databases are identical to the backups
    • Sourcegraph's data is restored and verified

Follow up

@vercel

vercel Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
sourcegraph-docs Ready Ready Preview Nov 13, 2026 2:15am UTC

Request Review

@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ This PR adds 1 duplicated pair(s) of passages

Passages this PR added, with the places that already say the same thing. Each added passage also has an inline comment.

2 passages, at least 100% similar

Each fact has one home in the docs. Keep the passage on the page that owns the topic, and replace the others with a sentence and a link to it, so they cannot drift apart. Before writing a config snippet, search docs/ for the key and link to the existing one.

⚠️ This PR edits 2 passage pair(s) that were already duplicated

The passage you changed is also written somewhere else, so the two copies now disagree. Decide which page is the home for it, and replace the other copy with a link; or make the same change there too.

2 passages, at least 100% similar

2 passages, at least 95% similar

Reproduce locally with node dev/check-duplicates.mjs --diff <(git diff -U0 origin/main).

@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor
Page Before After Diff
/self-hosted/deploy/docker-compose/operations prod preview −147 +73
/self-hosted/deploy/kubernetes/operations prod preview −115 +79
/self-hosted/how-to prod preview −1
/self-hosted/how-to/postgres14-index-corruption 🗑️ prod — −57
/self-hosted/how-to/rebuild-corrupt-postgres-indexes prod preview −1 +3
/self-hosted prod preview −1

@github-actions

This comment has been minimized.

@marcleblanc2

Copy link
Copy Markdown
Contributor Author

/spell PGDATA pgdata vacuumdb


```sql
create extension amcheck;
Ensure the `sourcegraph_db.out`, `codeintel_db.out` and `codeinsights_db.out` files are moved to a safe and secure location.

@github-actions github-actions Bot Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This passage was already duplicated, and is now out of step with:

The passage you changed is also written somewhere else, so the two copies now disagree. Decide which page is the home for it, and replace the other copy with a link; or make the same change there too.

@marcleblanc2 marcleblanc2 changed the title Fix psql ON_ERROR_STOP in database restore commands Fix broken database backup / restore commands Nov 13, 2026
marcleblanc2 and others added 20 commits November 12, 2026 20:13
Scaling down only sourcegraph-frontend left the worker and other
services connected. They reconnect right after pg_terminate_backend, and
can write into the new database while the dump loads: one test restore
failed with a duplicate key on critical_and_site_config_pkey. Scale every
Sourcegraph Deployment and StatefulSet except the three databases to 0
instead, which also replaces restarting the database StatefulSets.

Drop each database with DROP DATABASE ... WITH (FORCE), which
disconnects its sessions and drops it in one statement, and say on the
machine where kubectl runs where the dump files go.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
Co-authored-by: Amp <amp@ampcode.com>
The new-environment and existing-environment restore procedures were
the same steps written twice. On Kubernetes they are now identical,
since the existing-environment steps no longer delete the database
volumes. On Docker Compose only the first step differed, so it becomes
a conditional step: stop the deployment and remove the volumes when
restoring into an existing one.

Also drop the stale `docker-compose ps` sample output from the restore
steps; it listed services that no longer exist.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
The steps used `$pgsql_POD_NAME`, `$codeintel-db_POD_NAME`, and
`$codeinsights-db_POD_NAME`, which the page never defines. The last two
are not valid shell variables either: `$codeintel-db_POD_NAME` expands
`$codeintel` and leaves `-db_POD_NAME`. The databases are StatefulSets
with one replica in both the Helm chart and deploy-sourcegraph-k8s, so
their pods are always `pgsql-0`, `codeintel-db-0`, and
`codeinsights-db-0`.

`kubectl cp` also drops the `$NAMESPACE/` prefix, so every command
takes `-n $NAMESPACE` the same way, as the note says.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
Scaling it to zero stops its connections just as well, and the final
step of applying the manifests scales it back up. Deleting it leaves
Helm and Kustomize users to recreate it.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
The docs site doesn't wrap code blocks, so these commands scrolled sideways: up to 253 characters, in a block that fits 75 at desktop width. Each line is now at most 67 characters. The commands pass the same arguments as before.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1d140-e6de-7173-8b4f-187543b7833c
Co-authored-by: Amp <amp@ampcode.com>
Backup:
- Drop scaling down the frontend. pg_dump takes a consistent snapshot
  of each database while services stay connected, and scaling down only
  the frontend left the worker and others connected anyway. The backup
  also never scaled the frontend back up.
- Step A asks for a baseline of the running pods and their replicas.
- The amcheck step says it blocks writes to each table while it checks
  that table's indexes, since services now keep running.

Restore:
- Run pg_dump and psql directly with kubectl exec, without bash -c, so
  no quotes nest.
- Deleting the dump files from the volumes is its own step, after all
  three restores succeed.
- Restart the services with helm upgrade, which sets the replicas that
  the scale-down step zeroed back to the values; Kustomize users keep
  the applying manifests link.

Run as written on m6 (Helm 8.2.0, Helm 4.3.1), with the backup taken
while every service ran: all exit codes 0, no ERROR lines, the three
re-dumps identical to the backup, GraphQL data matches the seeded data,
and every workload back at 1/1.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
"Access the database" showed `pgsql-76a4bfcd64-rt4cn`, a pod name from
when pgsql was a Deployment; it is a StatefulSet, so its pod is always
`pgsql-0`. The restore's wait step now names the database pods too.

Drop the "Migrate to Kustomize" and "Upgrade" sections, which only
linked elsewhere; Kustomize is being deprecated.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
The amcheck SQL had no command to run it. Save it as amcheck.sql and
pipe it into psql on each database pod, with ON_ERROR_STOP=1 so a
failed check exits 3 instead of 0, and stdout discarded so only errors
show. The restore points at the backup's commands instead of repeating
the query.

- `create extension if not exists`: amcheck already exists in all
  three databases, so the plain form failed every time.
- `c.relkind = 'i'`: on codeintel-db the check failed with "only B-Tree
  indexes are supported as targets for verification" on the
  partitioned index codeintel_documents_pkey.

Step A now asks to verify the pods, especially the three databases.

Tested on m6: exit 0 on all three databases; without the relkind
filter, codeintel-db prints the error and exits 3.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
Drop the backup step that stopped the deployment and started only the
databases. pg_dump takes a consistent snapshot of each database while
services stay connected, and the backup never started the deployment
again, so following it left Sourcegraph down. This matches the
Kubernetes page.

Use the `docker compose` plugin instead of the old standalone
`docker-compose` command in the examples.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
AGENTS.md asks for sequentially numbered ordered Markdown lists. The
Kubernetes page lettered its steps, the only page in the repo to do so,
and the Docker Compose page escaped its numbers (`1\.`) so they were not
lists. Both now use `1.`, `2.`, `3.`.

The Kubernetes restore also ends with the Docker Compose page's last
step: browse to Sourcegraph and check the configuration came back.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
The sample was docker-compose v1 output from an old release, listing
jaeger and query-runner, which 8.2.0 no longer runs. Replace it with
output captured from a fresh 8.2.0 stack. The command prints only names
and status, since plain `docker compose ps` prints pinned image digests
and runs past 200 characters.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
The same two fixes as the Kubernetes operations page:
- `create extension if not exists amcheck`: the plain form errors on
  databases that already have amcheck.
- `c.relkind = 'i'`: without it the check stops on codeintel-db with
  "only B-Tree indexes are supported as targets for verification" at
  the partitioned index codeintel_documents_pkey, which reads like
  corruption and leaves the remaining indexes unchecked.

Tested on m6 (8.2.0): both pages' queries exit 0 with no errors on all
three databases.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
docker exec runs them directly, as the Kubernetes page does with
kubectl exec, so no quotes nest.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
It covers a bug in PostgreSQL 14.0 to 14.3. Sourcegraph now requires
PostgreSQL 16, so no supported deployment can hit it. Remove the two
links to it, and redirect its old URLs to "How to rebuild corrupt
Postgres indexes".

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
…ns page

It only restated that the frontend migrates on startup and linked to the
migrator docs.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
Drop the trailing comments, which only repeated the container name and
pushed the lines past the width of a code block.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
The restore drops and recreates each database, and
db-only-migrate.docker-compose.yaml starts only the three databases, so
nothing else is connected to block the dump's DROP DATABASE. Deleting
the volumes is unnecessary, as on the Kubernetes page; stopping the
deployment is still needed so nothing writes during the restore.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
The docker compose ps sample now comes from an 8.2.0 stack started with
the Jaeger and executor overrides, so it lists `jaeger` and `executor`.
Plain `docker compose ps` lists them too.

Plain `docker compose down` and `up -d` ignore those override files:
jaeger and executor kept running through a test restore, and the final
`up -d` recreated the frontend without `JAEGER_SERVER_URL`. The restore
now says to pass the same `-f` options as when starting Sourcegraph.

Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a1d197-7a9c-71fc-9998-6e55df9c47d0
@marcleblanc2
marcleblanc2 force-pushed the marc/fix-psql-on-error-stop branch from 3529555 to 5698cb0 Compare November 13, 2026 02:14
@marcleblanc2
marcleblanc2 added this pull request to stack #2159 November 13, 2026 02:14
marcleblanc2 added a commit to sourcegraph/deploy-sourcegraph-docker that referenced this pull request Nov 13, 2026
### Context

`db-only-migrate.docker-compose.yaml` starts only the databases, for
backups, restores, and upgrades

### Problems

- It was never updated to include `codeinsights-db` since it was newly
added

- It had duplicate content, and wasn't kept up to date
with`docker-compose.yaml`

### Solutions

- All three databases now use `extends`, which copies each service's
definition from `docker-compose.yaml`

- [sourcegraph/docs#2156](sourcegraph/docs#2156)
fixes the restore commands on that docs page, and needs this PR

### Checklist

- [x] All images have a valid tag and SHA256 sum (no image changes)

### Test plan

On a test machine (Docker 29.9.3, Compose 5.7.1): a full 8.2.0
deployment, `down`, then this file alone on the same volumes.

| | `main` | This PR |
| --- | --- | --- |
| Containers started | `pgsql`, `codeintel-db` | `pgsql`,
`codeintel-db`, `codeinsights-db`, nothing else |
| The three `pg_dump` commands from the docs | The third fails: `No such
container: codeinsights-db` | All three exit 0 |
| `docker compose -f docker-compose.yaml run --rm --no-deps migrator` |
Fails: `lookup codeinsights-db ... no such host` | `Schema(s) are
up-to-date!` |

- Followed the docs' backup and both restore procedures, with
[docs#2156](sourcegraph/docs#2156) at e40ca3ee:
every command exited 0, re-dumps were byte-identical to the backups, and
the code insights data came back
- `docker-compose config --hash` is identical from either file, so the
final `docker-compose up -d` recreated none of the three databases. On
`main`, it recreated `pgsql` and `codeintel-db`
- `config --services` lists only the three databases on Compose 2.18.1
and 5.7.1
- CI: [build
16917](https://buildkite.com/sourcegraph/deploy-sourcegraph-docker/builds/16917)
passed

Closes FE-580
@marcleblanc2
marcleblanc2 enabled stacked auto-merge (squash) November 13, 2026 03:09
@marcleblanc2
marcleblanc2 requested a review from a team November 13, 2026 03:10
@marcleblanc2
marcleblanc2 merged commit d502438 into main Nov 13, 2026
9 checks passed
@marcleblanc2
marcleblanc2 deleted the marc/fix-psql-on-error-stop branch November 13, 2026 03:12
marcleblanc2 added a commit that referenced this pull request Nov 13, 2026
Stacked on #2156: merge that first. GitHub then changes this PR's base
to `main`.

## Use `docker compose` instead of `docker-compose`

Compose v1 (`docker-compose`) is end of life, so every `docker-compose
<subcommand>` command now uses `docker compose`. File names
(`docker-compose.yaml`), the `deploy-sourcegraph-docker/docker-compose`
directory, volume names, and prose about Docker Compose deployments are
unchanged.

The DigitalOcean, Google Cloud, and Azure startup scripts downloaded the
standalone v1.29.2 binary, which doesn't provide `docker compose`. They
now install `docker-compose-plugin` from Docker's apt repository,
alongside `docker-ce`.

The generated `observability/alerts.mdx` and `dashboards.mdx` pages only
mention docker-compose in prose, so they need no change in
sourcegraph/sourcegraph.

## Add `--remove-orphans` to full-stack `docker compose up -d` commands

So containers for services that were removed from `docker-compose.yaml`
(or disabled in an override) get cleaned up instead of left running.
This matches the commands already used in the upgrade, configuration,
Redis, and cloud provider pages.

Left unchanged on purpose:

- Commands that start one service or a subset (`up -d migrator`, `up -d
pgsql codeintel-db codeinsights-db`).
- Commands that use a different compose file (`-f
db-only-migrate.docker-compose.yaml`, `-f
executor.docker-compose.yaml`). With `--remove-orphans`, those would
remove every container in the project that the smaller file doesn't
define.

---------

Co-authored-by: Amp <amp@ampcode.com>

This branch was successfully deployed

1 active deployment
Preview — 5698cb05 Deployed Nov 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants