Skip to Content

Duplicate an Odoo database

duplicate_database.sh copies an Odoo database to a new name on the same PostgreSQL server using createdb --template by default. It includes the filestore by default. Existing destination databases and, in full-copy mode, existing destination filestores are rejected. The source is not modified and existing connections are not terminated.

./duplicate_database.sh example_source example_copy
./duplicate_database.sh --nf example_source example_copy
./duplicate_database.sh --dr example_source example_copy
./duplicate_database.sh --dr --nf example_source example_copy
./duplicate_database.sh --filestore /path/to/data/filestore example_source example_copy

The default template copy avoids exporting and rebuilding the database. It requires all source connections to be closed, including idle sessions, and ownership of the source database (or PostgreSQL superuser privileges). PostgreSQL enforces these requirements; the script does not disconnect users or automatically fall back to another method.

Use --dr to select the previous pg_dump/pg_restore method. It permits connections to the source and takes a consistent database snapshot, but full database/filestore consistency still requires stopping writers. Both methods support --nf independently and reset the copied database's UUID.

The filestore root defaults to /srv/pmiverse/_pmisuite18/data/filestore. Override it with FILESTORE_BASE or --filestore (the CLI option wins). The source directory is <root>/<source> and the destination is <root>/<target>. A missing source filestore is an error in full-copy mode; use --nf if the database intentionally has none. --nf does not inspect or modify filestores; attachments stored in the database remain available, but file-backed attachments will be unavailable unless you separately supply the matching filestore.

Prerequisites and operation

  • Use Bash, GNU coreutils/find, PostgreSQL 13 or later, and PostgreSQL client tools compatible with the server. Run as the account that runs Odoo, with permission to read the source database and filestore, create databases, and write the filestore root. The new database and copied files belong to the account performing the operation. Template copying preserves ownership and grants on objects inside the database; --dr restores objects under the current PostgreSQL role without copying their ownership or grants.
  • Connections use PostgreSQL's usual environment variables, including PGHOST, PGPORT, PGUSER, and PGPASSFILE. The maintenance database is postgres. The script does not read Odoo configuration or prompt for passwords.
  • Stop Odoo and other writers, including scheduled jobs, before copying, and keep them stopped until completion. In template mode, close all source connections as well. PostgreSQL's database copy is consistent, but the filesystem copy is separate; concurrent writes or attachment garbage collection can leave the two out of sync. The script does not stop services itself.
  • Keep the destination offline during the operation. Reserve its name for this operation: do not run a concurrent restore or other database/filestore manager against that destination. Names support ASCII letters, digits, underscores, dots, and hyphens, up to 63 characters, starting with a letter, digit, or underscore.
  • Allow disk space for the new database and the copied filestore. With --dr, also allow space for a temporary database dump. Template mode creates no temporary dump. Temporary dumps use TMPDIR (default /tmp) and are removed on normal exit, error, or handled signals. A forced kill or host crash can leave a private pmis-duplicate.* directory requiring manual cleanup.

The copy receives a new database.uuid, as an independent Odoo database should. Other application settings, users, credentials, scheduled actions, and integration configuration are copied unchanged. The script does not neutralize the copy. Production-derived copies must remain offline until neutralized with the existing restore tool's --only-neutralize mode, using the correct destination database and Odoo configuration. Inspect installed custom integrations before enabling the copy.

If duplication fails after database creation, the new database and any copied filestore are retained for inspection. They may be incomplete: keep them offline, inspect the error, and explicitly clean up the failed destination before retrying. The script never drops a database or removes a destination filestore automatically.

Bash completion

The shared PMIVerse Bash completion provides options, directory completion after --filestore, and live source database names from PostgreSQL. Press Tab twice at the source argument to list candidates, or type a prefix and press Tab. The read-only query connects to postgres using the same PostgreSQL environment variables as the script, so it lists local databases by default and follows PGHOST, PGPORT, and PGUSER overrides. It excludes PostgreSQL templates, postgres, databases that disallow connections, and names the script cannot accept.

Completion never prompts for a password and limits the query to three seconds using GNU timeout. If PostgreSQL is unavailable, authentication fails, the query times out, or the required tools are missing, it silently falls back to the configured PMIVerse registry. Options, directories, and destination arguments do not query PostgreSQL. Enter the destination name yourself, since it must not already exist. Completion works when invoking the script by name or path.

With bash-completion installed, enable it in the current Bash session:

source /usr/share/bash-completion/bash_completion
source /srv/pmiverse/_pmiconfig/scripts/completion/pmiverse-completion.bash

Reload the shared file after completion changes; an already open shell retains its previous definitions. The canonical absolute script path is explicitly registered as well, replacing any cached generic completion for that path. Type a space after the command before pressing Tab twice, for example ./duplicate_database.sh followed by Tab, Tab. The extensionless name duplicate_database is also registered for users who expose the script through an alias or symlink with that name. Completion does not create that shortcut.

The existing CLI shortcut installer also installs the updated shared completion file for future sessions.

Validation

Run the isolated contract tests from the repository root:

bash tests/test_duplicate_database.sh
bash tests/test_duplicate_completion.sh

The tests replace PostgreSQL commands with mocks and use synthetic filestore fixtures under /tmp; they do not connect to a real database or instance.