This topic describes how to export a PostgreSQL VDB or dSource snapshot to a standalone physical PostgreSQL host using the DCT API. V2P export produces a fully independent, non-virtualized copy of the database on the target host. The exported database starts in read-write mode and retains the archive mode, configuration, and encryption or TLS state of the source.
Prerequisites
-
Delphix Continuous Data Engine 2026.4.0.0 or later.
-
PostgreSQL connector 2026.2.0 uploaded to the engine.
-
Delphix Virtualization SDK 5.1.0.
-
The target physical host meets the following conditions:
-
The target directory is empty or has the correct permissions for the Delphix OS user.
-
The specified port is free and not in use by any existing PostgreSQL instance.
-
No existing PostgreSQL instance with the same name or alias is running on the target host.
-
-
You have a valid VDB or dSource snapshot to export. Using a VDB snapshot is the recommended approach.
Target host prerequisites
Before calling any export API, confirm the following on the target host:
-
The target directory already exists, and the OS user the connector runs as has read, write, and execute access to it.
-
The target directory is clean — it must not contain anything other than what a prior Delphix rsync/connector run would leave (a
data/directory, apostgres_init/path, a hidden.delphix/directory, or a filesystem artifact such aslost+foundon a dedicated mount). Any other content is treated as a dirty target and the export is refused. -
No leftover, partially-exported data directory from a previous failed attempt is present at the target location.
-
The requested PostgreSQL port is not already in use by another process.
-
The PostgreSQL binaries installed on the target host are the same major version as the source snapshot. A mismatch prevents the export from completing.
-
If a Postgres cluster is already running against the target data directory, it must be serving on the same port being requested — the connector treats a running cluster on a different port than requested as a misconfiguration, not a success.
The target directory itself is not a connector-level setting — it comes directly from the API's own targetDirectory field (the engine's rsync target). It is authoritative regardless of any value a plugin might declare internally.
Perform a V2P export from a VDB
Use one of the following DCT API endpoints to export a VDB to a physical PostgreSQL host:
-
POST /vdbs/{vdbId}/export-by-snapshot— Export a specific VDB snapshot. -
POST /vdbs/{vdbId}/export-from-bookmark— Export from a bookmark. -
POST /vdbs/{vdbId}/export-by-timestamp— Export from a point in time.
In the request body, include the appdata_v2p_params object with the following fields:
-
postgresPort— The port on the target host where the exported PostgreSQL instance will listen. -
privilegedOsUser— (Optional) The OS user used for privilege elevation on the target host during export. Required when the Delphix OS user does not have direct write access to the target directory. -
configSettingsStg— (Optional) Additional PostgreSQL configuration parameters to apply on the exported instance viavirtualToPhysicalDefinition.
targetDirectory value is provided by the engine via physical_source.target_directory and is not a connector-defined field. Do not set this field in the request body.Perform a V2P export from a dSource
Use one of the following DCT API endpoints to export a dSource snapshot to a physical PostgreSQL host:
-
POST /dsources/{dsourceId}/export-by-snapshot— Export a specific dSource snapshot. -
POST /dsources/{dsourceId}/export-from-bookmark— Export from a bookmark. -
POST /dsources/{dsourceId}/export-by-timestamp— Export from a point in time.
The appdata_v2p_params fields are the same as for VDB export. See Perform a V2P export from a VDB for field descriptions.
Request parameters
Every export call takes these fields, common across all six endpoints:
| Field | Required | Description |
|---|---|---|
repository_id
|
Yes | Target host repository (installed PostgreSQL binaries) to export onto. |
targetDirectory
|
Yes | Absolute path on the target host where the physical data directory will be placed. Must be a clean, accessible directory (see prerequisites). |
database_name
|
Depends on endpoint/source — include for dSource exports | Logical name for the exported physical database instance. |
appdata_config_params.name
|
Yes | Connector-internal config name for the exported instance. |
appdata_v2p_params.postgresPort
|
Yes | Port the physical PostgreSQL instance will listen on after export. No fixed default is guaranteed by the API — always supply it explicitly. |
appdata_v2p_params.privilegedOsUser
|
No | Unix username used for privilege elevation during export (for example, changing file ownership). Omit to use the connector's default OS user. |
appdata_v2p_params.configSettingsStg
|
No | Array of {propertyName, value, commentProperty} entries — additional postgresql.conf overrides applied to the physical instance. |
Selector fields, one set per endpoint:
| Endpoint suffix | Additional required fields |
|---|---|
export-by-snapshot
|
snapshot_id
|
export-from-bookmark
|
bookmark_id
|
export-by-timestamp
|
timeflow_id, timestamp (ISO-8601, must fall within that timeflow's data) |
bookmark_id values come from the DCT bookmarks API (POST /v3/bookmarks with snapshot_ids: [...], or list existing ones with GET /v3/bookmarks). timeflow_id is the dSource's or VDB's current timeflow, available on the dataset's detail endpoint (current_timeflow_id).
Example requests
dSource V2P export by snapshot
curl -sk -X POST \
"https://<dct-host>/v3/dsources/{dsourceId}/export-by-snapshot" \
-H "Authorization: apk <api-key>" \
-H "Content-Type: application/json" \
-d '{
"repository_id": "<repository_id>",
"snapshot_id": "<snapshot_id>",
"database_name": "v2p_export",
"targetDirectory": "/mnt/provision1",
"appdata_config_params": { "name": "v2p_export" },
"appdata_v2p_params": { "postgresPort": 5436 }
}'
dSource V2P export from bookmark
curl -sk -X POST \
"https://<dct-host>/v3/dsources/{dsourceId}/export-from-bookmark" \
-H "Authorization: apk <api-key>" \
-H "Content-Type: application/json" \
-d '{
"repository_id": "<repository_id>",
"bookmark_id": "<bookmark_id>",
"database_name": "v2p_export",
"targetDirectory": "/mnt/provision1",
"appdata_config_params": { "name": "v2p_export" },
"appdata_v2p_params": { "postgresPort": 5437 }
}'
dSource V2P export by timestamp
curl -sk -X POST \
"https://<dct-host>/v3/dsources/{dsourceId}/export-by-timestamp" \
-H "Authorization: apk <api-key>" \
-H "Content-Type: application/json" \
-d '{
"repository_id": "<repository_id>",
"timeflow_id": "<timeflow_id>",
"timestamp": "2026-07-28T09:19:23.000Z",
"database_name": "v2p_export",
"targetDirectory": "/mnt/provision1",
"appdata_config_params": { "name": "v2p_export" },
"appdata_v2p_params": { "postgresPort": 5438 }
}
VDB — V2P export by snapshot / bookmark / timestamp
The vdbs/{vdbId} endpoints accept the same fields, minus database_name:
curl -sk -X POST \
"https://<dct-host>/v3/vdbs/{vdbId}/export-by-snapshot" \
-H "Authorization: apk <api-key>" \
-H "Content-Type: application/json" \
-d '{
"repository_id": "<repository_id>",
"snapshot_id": "<snapshot_id>",
"targetDirectory": "/mnt/provision2",
"appdata_config_params": { "name": "v2p_vdb_export" },
"appdata_v2p_params": { "postgresPort": 5441 }
}'
Swap snapshot_id for bookmark_id (export-from-bookmark) or for timeflow_id + timestamp (export-by-timestamp), same as the dSource examples above.
A successful call returns a job object with status: STARTED; poll the job by id to track progress. Only one export job may run at a time per source dataset.
Engine API (direct)
The DCT API wraps the Delphix Engine's own REST API. Environments that talk to the Engine directly instead of through DCT can drive the same V2P workflow at that lower level. Object references here are Engine-native (e.g. APPDATA_CONTAINER-104, APPDATA_REPOSITORY-21) rather than DCT's x-ref IDs, and a couple of field/type names differ from the DCT surface above — called out below.
Session bootstrap
The Engine API is session/cookie based, not API-key based:
ENG=https://<engine-host>
CJ=/tmp/dlpx_cookies.txt
curl -sk -c "$CJ" -X POST "$ENG/resources/json/delphix/session" \
-H "Content-Type: application/json" \
-d '{"type":"APISession","version":{"type":"APIVersion","major":1,"minor":11,"micro":18}}'
curl -sk -b "$CJ" -c "$CJ" -X POST "$ENG/resources/json/delphix/login" \
-H "Content-Type: application/json" \
-d '{"type":"LoginRequest","username":"<user>","password":"<password>"}'
dSource — V2P export by snapshot
curl -sk -b "$CJ" -X POST "$ENG/resources/json/delphix/database/export" \
-H "Content-Type: application/json" \
-d '{
"type": "AppDataExportParameters",
"sourceConfig": {
"type": "AppDataStagedSourceConfig",
"repository": "<repository-ref>",
"name": "v2p_export",
"parameters": { "name": "v2p_export" }
},
"timeflowPointParameters": {
"type": "TimeflowPointSnapshot",
"snapshot": "<snapshot-ref>"
},
"filesystemLayout": {
"type": "AppDataFilesystemLayout",
"targetDirectory": "/mnt/provision1"
},
"parameters": { "postgresPort": 5435 }
}'
curl -sk -b "$CJ" "$ENG/resources/json/delphix/job/<job-ref>"
VDB — V2P export by snapshot
Identical call, with two differences: sourceConfig.type is AppDataDirectSourceConfig — a VDB's own source config type, distinct from a dSource's AppDataStagedSourceConfig — and the snapshot ref points at the VDB's own container/timeflow, not the dSource's.
curl -sk -b "$CJ" -X POST "$ENG/resources/json/delphix/database/export" \
-H "Content-Type: application/json" \
-d '{
"type": "AppDataExportParameters",
"sourceConfig": {
"type": "AppDataDirectSourceConfig",
"repository": "<repository-ref>",
"name": "v2p_vdb_export",
"parameters": { "name": "v2p_vdb_export" }
},
"timeflowPointParameters": {
"type": "TimeflowPointSnapshot",
"snapshot": "<vdb-snapshot-ref>"
},
"filesystemLayout": {
"type": "AppDataFilesystemLayout",
"targetDirectory": "/mnt/provision2"
},
"parameters": { "postgresPort": 5436 }
}'
If no VDB exists yet to export from, provision one first:
curl -sk -b "$CJ" -X POST "$ENG/resources/json/delphix/database/provision" \
-H "Content-Type: application/json" \
-d '{
"type": "AppDataProvisionParameters",
"container": {
"type": "AppDataContainer",
"name": "<vdb-name>",
"group": "<group-ref>",
"sourceConfig": {
"type": "AppDataDirectSourceConfig",
"repository": "<repository-ref>",
"name": "<vdb-name>",
"parameters": { "name": "<vdb-name>" }
},
"source": {
"type": "AppDataVirtualSource",
"allowAutoVDBRestartOnHostReboot": false,
"parameters": { "mountLocation": "/mnt/vdb1", "postgresPort": 5440 }
}
},
"source": {
"type": "AppDataVirtualSource",
"allowAutoVDBRestartOnHostReboot": false,
"parameters": { "mountLocation": "/mnt/vdb1", "postgresPort": 5440 }
},
"sourceConfig": {
"type": "AppDataDirectSourceConfig",
"repository": "<repository-ref>",
"name": "<vdb-name>",
"parameters": { "name": "<vdb-name>" }
},
"timeflowPointParameters": {
"type": "TimeflowPointSnapshot",
"snapshot": "<dsource-snapshot-ref>"
}
}'
allowAutoVDBRestartOnHostReboot and the top-level source field are both required by this endpoint but easy to miss — the Engine's own validation error is the fastest way to discover them if a first attempt is rejected.
V2P export from bookmark
Bookmarks live under /delphix/timeflow/bookmark, not /delphix/bookmark. The timeflowPoint field on the create call uses a different type, AppDataTimeflowPoint — not the TimeflowPointSnapshot / TimeflowPointTimestamp types used by the export call itself:
# Create the bookmark
curl -sk -b "$CJ" -X POST "$ENG/resources/json/delphix/timeflow/bookmark" \
-H "Content-Type: application/json" \
-d '{
"type": "TimeflowBookmarkCreateParameters",
"name": "v2p-bookmark-1",
"timeflowPoint": {
"type": "AppDataTimeflowPoint",
"timeflow": "<timeflow-ref>",
"timestamp": "2026-07-31T22:37:30.520Z"
}
}'
# -> returns a TIMEFLOW_BOOKMARK-<n> reference
# Export from it
curl -sk -b "$CJ" -X POST "$ENG/resources/json/delphix/database/export" \
-H "Content-Type: application/json" \
-d '{
"type": "AppDataExportParameters",
"sourceConfig": {
"type": "AppDataStagedSourceConfig",
"repository": "<repository-ref>",
"name": "v2p_export_bookmark",
"parameters": { "name": "v2p_export_bookmark" }
},
"timeflowPointParameters": {
"type": "TimeflowPointBookmark",
"bookmark": "<bookmark-ref>"
},
"filesystemLayout": {
"type": "AppDataFilesystemLayout",
"targetDirectory": "/mnt/provision1"
},
"parameters": { "postgresPort": 5435 }
}'
List or delete bookmarks with:
curl -sk -b "$CJ" "$ENG/resources/json/delphix/timeflow/bookmark?database=<container-ref>"
curl -sk -b "$CJ" -X POST "$ENG/resources/json/delphix/timeflow/bookmark/<bookmark-ref>/delete" \
-H "Content-Type: application/json" -d '{}'
V2P export by timestamp
Get a snapshot's exact UTC timeflow point from the snapshot's own detail endpoint rather than the list endpoint — the list view's timestamp string is ambiguous about timezone, while the detail view's latestChangePoint is always UTC (Z-suffixed):
curl -sk -b "$CJ" "$ENG/resources/json/delphix/snapshot/<snapshot-ref>"
# -> result.latestChangePoint.timestamp (e.g. "2026-07-31T22:37:30.520Z")
# -> result.latestChangePoint.timeflow (e.g. "APPDATA_TIMEFLOW-176")
curl -sk -b "$CJ" -X POST "$ENG/resources/json/delphix/database/export" \
-H "Content-Type: application/json" \
-d '{
"type": "AppDataExportParameters",
"sourceConfig": {
"type": "AppDataStagedSourceConfig",
"repository": "<repository-ref>",
"name": "v2p_export_timestamp",
"parameters": { "name": "v2p_export_timestamp" }
},
"timeflowPointParameters": {
"type": "TimeflowPointTimestamp",
"timeflow": "<timeflow-ref>",
"timestamp": "2026-07-31T22:37:30.520Z"
},
"filesystemLayout": {
"type": "AppDataFilesystemLayout",
"targetDirectory": "/mnt/provision1"
},
"parameters": { "postgresPort": 5435 }
}'
A timestamp outside the timeflow's actual data range is rejected with the valid range in the error message (exception.database.timestamp.cannot.be.provisioned.from).
CD Engine vs. DCT: field/type differences at a glance
| Concept | DCT API | CD Engine API |
|---|---|---|
| Auth | API key header | Session cookie (/session + /login) |
| dSource source config type | n/a (abstracted) | AppDataStagedSourceConfig
|
| VDB source config type | n/a (abstracted) | AppDataDirectSourceConfig
|
| Snapshot selector | snapshot_id
|
timeflowPointParameters: {type: TimeflowPointSnapshot, snapshot}
|
| Bookmark selector | bookmark_id
|
timeflowPointParameters: {type: TimeflowPointBookmark, bookmark}
|
| Timestamp selector | timeflow_id + timestamp |
timeflowPointParameters: {type: TimeflowPointTimestamp, timeflow, timestamp}
|
| Bookmark creation | POST /v3/bookmarks
|
POST /delphix/timeflow/bookmark with a timeflowPoint of type AppDataTimeflowPoint |
| Target directory | targetDirectory
|
filesystemLayout: {type: AppDataFilesystemLayout, targetDirectory}
|
| Port/config overrides | appdata_v2p_params.*
|
parameters.postgresPort / parameters.configSettingsStg |
What happens during export
-
The engine rsyncs the selected snapshot's data read-only into
targetDirectoryon the target host. -
The connector validates the target: directory exists and is clean, OS user has access, port is free, and the target host's installed PostgreSQL major version matches the snapshot's.
-
The connector rewrites
postgresql.conffor the physical instance: the requested port, any user-suppliedconfigSettingsStgoverrides, and a fixed set of physical-mode settings — replication/recovery parameters (archive_command,restore_command,primary_conninfo, and so on) are disabled,archive_modeandsslare turned off, andhot_standbyis turned off. This mirrors the state of the source VDB — Delphix does not enable archiving or TLS on VDBs, so the physical export inherits the same posture. -
Snapshot-runtime artifacts (
standby.signal,recovery.conf,recovery.signal,postmaster.opts,postmaster.pid, andpostgresql.auto.conf) are cleared or renamed so the cluster is not mistaken for a live replica on startup. -
The physical PostgreSQL cluster is started in full read-write mode. This step is idempotent — if the cluster is already running on the requested port, the export is treated as already complete.
Recovery/startup settings (recovery_init_sync_method, log_startup_progress_interval) are not re-applied by the V2P export step itself; they carry over automatically because V2P rsyncs the already-configured postgresql.conf from the source VDB or dSource snapshot before layering its own physical-mode overrides on top.
Troubleshooting
| Condition | Cause | Resolution |
|---|---|---|
| Directory not found | targetDirectory does not exist on the target host, or the expected data/ sub-directory is missing after rsync. |
Create the directory (or verify the rsync step completed) and retry. |
| Inaccessible mount point | The OS user does not have read/write/execute access to targetDirectory. |
Correct ownership/permissions and retry. |
| Target directory not empty | targetDirectory contains content other than the expected connector artifacts. |
Point the export at a genuinely clean directory. |
| Port in use | The requested postgresPort is occupied on the target host. |
Choose a free port, or free the existing one, and retry. |
| Version mismatch | The snapshot's PostgreSQL major version differs from the version installed at repository_id on the target host. |
Install matching PostgreSQL binaries on the target host and retry. |
| Config file not found | postgresql.conf is missing from the rsynced data directory. |
Verify the rsync/export step completed successfully. |
| Port mismatch on an already-running cluster | A Postgres cluster is already running for the target data directory, but not on the requested port. | Stop the existing cluster, or re-issue the export with the port it is actually running on. |
Expected post-export state
After a successful V2P export:
-
The exported PostgreSQL instance runs in read-write mode on the target host.
-
The instance retains the archive mode, initialization parameters, and encryption or TLS state of the source VDB or dSource.
-
The instance is fully independent of Delphix storage; no ongoing connection to the Delphix engine is required.
-
V2P is a one-shot export operation. Recovery or restore operations must be performed at the dSource or VDB level before export.
V2P workflow limitations
| Limitation | Description | Workaround |
|---|---|---|
| No V2P UI | Virtual-to-Physical export cannot be initiated from the DCT or Continuous Data Engine UI. | Use the DCT API export endpoints directly, or the DCT toolkit built on top of them. |
| No custom file mapping | V2P exports the entire snapshot to a single target directory. Per-file destination mapping (for example, splitting a large snapshot across multiple mount paths) is not supported in Virtualization SDK 5.1.0. | Provision a target directory large enough to hold the full snapshot. |
| One V2P export at a time per source | A second export call against the same dSource or VDB while one is already in progress is rejected. | Wait for the in-progress export job to complete (or fail) before retrying. |
| V2P is one-way | A physical database created by V2P cannot be re-ingested into Delphix, and no rollback/recovery is available on it after export. | Perform all recovery/restore operations at the dSource level, before exporting. |
| No TDE support | The PostgreSQL connector does not support Transparent Data Encryption (TDE). Encryption/TLS state carry-over during V2P export applies only to non-TDE settings already in use on the source. | Do not rely on V2P to carry over TDE-based encryption; none is supported by the connector. |