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;
--drrestores objects under the current PostgreSQL role without copying their ownership or grants. - Connections use PostgreSQL's usual environment variables, including
PGHOST,PGPORT,PGUSER, andPGPASSFILE. The maintenance database ispostgres. 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 useTMPDIR(default/tmp) and are removed on normal exit, error, or handled signals. A forced kill or host crash can leave a privatepmis-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.