Business team planning strategy on a wall of documents and charts — the migration-strategy whiteboard.

Enterprise Asset Migration: 7 Best Practices for Production Migrations

Battle-tested patterns for running zero-downtime, audit-safe asset migrations between any two platforms — at any scale.

Univaultport TeamMay 17, 20266 min readMedium · 12 min

Moving a million images from one DAM to another is not a one-shot script. It's a phased operation with cutover windows, rollback paths, and verification steps. These seven practices come from real production migrations across thousands of customer workloads.

1. Always start with a dry-run

Before any real migration, run a dry-run pass: enumerate the source, validate destinations, and produce a manifest without writing anything. A dry-run answers four questions:

  • How many assets are we actually moving?
  • What's the total byte volume?
  • Are there any unreadable objects, broken permissions, or malformed metadata?
  • Roughly how long will the real migration take?

Univaultport's dry-run mode produces a CSV-exportable manifest in under a minute for libraries up to ~100k assets. Use it as your migration plan of record — share it with stakeholders, get sign-off, then run the real job.

2. Make every migration idempotent

A migration job will be interrupted. The worker crashes, the network blips, the API rate-limits — these are not "if" events, they're "when". Your migration logic must handle being restarted from scratch without corrupting state.

The two key invariants:

1. The same asset migrated twice produces the same destination.
2. A partially-completed migration can be rerun safely.

Univaultport handles this for you via per-asset state tracking: every asset is recorded as pending → in-flight → done in Postgres, and reruns skip already-done assets unless you explicitly request a reset. This is the difference between a 12-hour migration that finishes and a 12-hour migration that you have to nervously babysit.

3. Chunk large migrations into bounded job sizes

A single migration job that processes 5 million assets is fragile. A failure 4.5M assets in is genuinely awful to diagnose.

Instead, chunk by source folder or by date range and run a series of smaller jobs:

Source: cloudinary://acme/marketing
  ├─ Job 1: /2026/01/
  ├─ Job 2: /2026/02/
  ├─ Job 3: /2026/03/
  └─ ...

Each chunk should ideally complete in under 4 hours. Smaller chunks mean:

  • Faster failure isolation
  • Cleaner rollback (re-run only the failed chunk)
  • Easier reasoning about cost (each chunk has known bytes/operations)
  • Lower wall-clock to first verified success

4. Respect every platform's rate limits

Every source and destination has API limits. Hit them and you'll see 429s cascading through the worker. Plan your migration speed around the slowest participant:

PlatformTypical limitNotes
Cloudinary500 req/hr (free), 2k+ paidAdmin API — listing + delete
S33,500 PUT/s per prefixPractically unlimited for normal workloads
Google Drive1,000 req/100s per userService account limits per project, not per SA
GCS5,000 ops/s per bucketPractically unlimited
WordPressdepends on hostMost hosts cap at a few requests per second

Univaultport's worker applies exponential back-off automatically, but the best mitigation is parallelism control. Set the concurrency to a value that respects the slower platform, not the faster one.

5. Preserve metadata, but verify what gets through

"Migrate the assets" is the easy part. "Migrate the metadata that goes with them" is what makes the migration usable post-cutover.

Each platform has different metadata semantics:

S3              → key, content-type, custom user metadata (limited to 2 KB)
Cloudinary      → tags, context, asset folder, transformations
Google Drive    → name, parent folders, MIME type, custom properties
WordPress       → title, alt-text, caption, description, taxonomies
Contentful      → fields per content model

Univaultport preserves what's lossless across the boundary and logs every lossy translation in the job log. Common examples:

  • Cloudinary transformations don't survive a move to S3 (they're applied client-side at delivery time)
  • Drive folder hierarchy collapses on WP (flat library)
  • AEM XMP metadata gets normalized to a subset on most other destinations

Plan for the lossy pieces before cutover. Decide which platform owns canonical metadata, and migrate references accordingly.

6. Audit-log every migration as it runs

Compliance and ops both need to answer the same questions after the fact:

  • Who triggered this migration?
  • What credentials were used?
  • Which assets moved where, and when?
  • Were any assets skipped, failed, or modified?

Univaultport records all of this in a structured, immutable per-job audit log, viewable in the job detail page and exportable as JSON. For SOC 2 / ISO 27001 customers, this log is the artifact your auditors want.

7. Plan the cutover, not just the data move

The data move is the easy half. The hard half is:

  1. What stops writing to the old system?
  2. What starts reading from the new one?
  3. What's the rollback plan if cutover fails?

The pattern that works best for production migrations:

  1. Initial bulk sync

    Full migration over a weekend (or whenever traffic is lowest). Source remains the system of record.
  2. Delta sync window

    Run a daily incremental migration of newly-changed assets for 1–2 weeks. This catches anything the bulk sync missed.
  3. Read cutover

    Switch your application's reads to the new system. The old system is still being written to.
  4. Write cutover

    Stop writes to the old system. Final delta sync. Confirm zero drift.
  5. Decommission

    After 30+ days of verified production use on the new system, archive (don't delete) the old one.

The temptation is always to skip step 5 and just delete the old system. Don't. The cost of keeping an archived copy for 90 days is trivial compared to the disaster of needing it after deletion.

Summary checklist

Before declaring a migration done, you should be able to answer "yes" to all of these:

  • Dry-run completed and reviewed
  • Migration is idempotent — can be safely restarted
  • Job is chunked to < 4 hours per chunk
  • Source and destination rate limits respected
  • Metadata translation losses documented
  • Audit log captures every transfer
  • Cutover plan has explicit rollback steps
  • Old system archived, not deleted

Where Univaultport fits

Univaultport handles practices 1, 2, 3 (mostly), 4, 5 (lossless parts), and 6 for you automatically. Practices 5 (lossy translation policy) and 7 (cutover plan) are organizational decisions — we give you the data and the logs; you decide the policy.

For most teams, that's the difference between a migration that takes a quarter to plan and weeks to execute, versus one that runs over a weekend with a clear audit trail.

Keep going

Related guides