DynamoDB has no one-click "copy table" command — not in the AWS CLI, not in the
console. Every copy is really two halves: get the data out of the source, and
load it into a destination table. The first half is where a GUI earns its keep —
a lossless, filtered, verified export — while the second half always runs on AWS
tooling. This guide covers both, and the operational gotchas that bite in production.
How do I copy a DynamoDB table to another account or region?
There's no native copy-table command. For the out half, pull a lossless
DynamoDB-JSON export of the table (one click in DynoTable, no script)
or snapshot it with the managed S3 export. For the in half, pick the AWS-side
approach that fits: a Scan + BatchWriteItem script for small one-off copies, an S3
export-then-import for large tables, AWS Backup copy + restore for cross-account
moves with full fidelity, or global tables for ongoing live replication. Each
restore or import creates a new table.
| Situation | Best approach |
|---|---|
| Small table, one-off, full control | Scan + BatchWriteItem script |
| Large table, can tolerate a snapshot | S3 export → import (creates new table) |
| Cross-account / cross-region with restore | AWS Backup copy + restore |
| Ongoing live replication (not a one-time copy) | Global tables |
No approach is universally "right" — it depends on table size, whether you need a
point-in-time snapshot or live data, and whether the destination is a new or existing
table.
The export half: pull the data with DynoTable
Before any restore can happen, the data has to leave the source — and a hand-rolled
scan loop is the most error-prone part of a small migration (dropped pages, mangled
number precision, type tags stripped too early). DynoTable does that
half in one click:
- Lossless by design: export the full filter match as marshalled DynamoDB-JSON — the type-wrapped wire form, which preserves large numbers (> 2⁵³) that plain JSON corrupts — or as NDJSON/CSV when the target isn't DynamoDB at all. See export to CSV for the format details.
-
Scoped, not all-or-nothing: the managed S3 export snapshots the whole
table; DynoTable exports every item your query matches, streamed straight
from DynamoDB — so "copy only the
TENANT#42items to staging" is a filter, not a script. - Big tables welcome: exports detach and run in the background, streaming to disk row by row — a multi-gigabyte pull survives tab switches and app reloads.
- Verification after the load: once the destination table is up, browse source and target side by side, compare item counts (table size & item count), and spot-check sample records — without writing a verification script.
The honest boundary: DynoTable pulls data out and verifies the result — the
load into the destination table runs on AWS tooling (a write script, S3 import,
or Backup restore), covered next.
Approach 1: Scan + BatchWriteItem (the script)
The lowest-tech option: read every item from the source with Scan, write it to the
destination with BatchWriteItem. Works cross-account and cross-region as long as your
script holds credentials for both sides (or assumes a role in the target account).
# Sketch — read source, write target (pseudo; use the SDK in real life)
aws dynamodb scan --table-name SourceTable --region us-east-1 \
> items.json
# transform Items[] into BatchWriteItem RequestItems, then:
aws dynamodb batch-write-item --request-items file://batch.json \
--region eu-west-1
The gotchas are real and easy to miss:
-
BatchWriteItemcaps at 25 items or 16MB per call — you must chunk, and a single call can return unprocessed items you have to retry with exponential backoff (API reference). -
Writes consume write capacity. On a provisioned target you'll hit
ProvisionedThroughputExceededExceptionfast; on-demand absorbs more but still caps each partition at a hard 1,000 WCU / 3,000 RCU limit. Size the write load against the destination's capacity before you start. -
Scanreads the whole table and meters every item — the classic Query-vs-Scan cost. A big table also means paginating throughLastEvaluatedKey; see pagination. - It's not atomic. Items written while the scan is in flight can be missed — you only get a consistent snapshot if the source is quiescent.
Best for small tables or when you need to transform/filter during the copy — and if
the read half already exists as a DynoTable marshalled export, the script shrinks to
just the chunk-and-write loop.
Approach 2: Export to S3, then import to a new table
For large tables, DynamoDB's managed export to S3 plus import from S3 avoids
hammering your capacity at all.
Export snapshots the table to an S3 bucket
(how it works):
- Requires point-in-time recovery (PITR) enabled on the source table.
- Does not consume read capacity and has no impact on table performance — it reads from continuous backups, not the live table.
- Outputs DynamoDB JSON or Amazon Ion format. (The DynamoDB-JSON wire format is what lands in S3, type tags and all.)
- Can write to an S3 bucket owned by another account and in a different region.
- Supports full and incremental exports (incremental export GA, Sept 2023).
Import then builds a fresh table from that S3 data
(how it works):
- Imports into a brand-new table only — you cannot import into an existing table.
- Does not consume write capacity on the new table.
- Accepts CSV, DynamoDB JSON, or Amazon Ion (optionally GZIP/ZSTD compressed).
- The source S3 bucket may be in another account or another region.
- You can define secondary indexes at import time, queryable as soon as the import completes.
Warning
Import overwrites duplicate keys silently — if the source has non-unique primary
keys, only the last write survives, and the processed-item count won't match the final
table count. Validate key uniqueness in the source first.
This is the cleanest path for a large cross-account/cross-region migration where a
point-in-time snapshot (not live data) is acceptable.
Approach 3: AWS Backup copy + restore
If you already use AWS Backup, it copies recovery points across accounts and regions
(cross-account migration guide):
- Back up the source table into a backup vault.
- Copy the backup to a vault in the target account/region.
- Restore it to a new table in the target.
Key constraints:
- Cross-account copy requires both accounts to be in the same AWS Organization.
- Restore always creates a new table — you can't restore over an existing one.
- Global secondary indexes are preserved by default (exclude some or all to save on restore time/cost); you can't add new indexes at restore.
- Encryption gotcha: to keep the same KMS key on a cross-region restore you need a multi-region key; for cross-account you must share the key with the target account. AWS-owned and AWS-managed keys can't be shared or made multi-region (restore encryption notes).
Tip
For a one-off cross-region restore with table-setting changes, AWS Backup is more
operationally complete than a hand-rolled script — it carries GSIs, encryption, and
metadata, not just item data.
Approach 4: Global tables (live replication, not a one-time copy)
Global tables replicate a table across regions — and now optionally across accounts
(multi-account GA, Feb 2026) —
continuously. Any replica serves reads and writes (multi-active), with asynchronous,
last-writer-wins replication
(global tables docs).
This is not a "copy and walk away" tool — it's ongoing replication. Use it when you
want the destination region to stay in sync indefinitely (DR, low-latency local reads),
not for a clean one-time migration. Add a region to an existing table and DynamoDB
backfills the existing data into the new replica.
Note
Global tables default to multi-region eventual consistency (MREC); a strong-consistency
mode (MRSC) exists but is same-account only. If you treat a global-table replica as a
migration target and cut over immediately, replication lag can mean the new region hasn't
caught up yet.
Operational gotchas (all approaches)
- GSIs aren't free to recreate. A scan+write copy doesn't carry indexes — you define them on the target and they backfill (and cost) separately. Plan your GSI vs LSI layout on the destination up front; LSIs can only be created at table-creation time (LSI docs).
- Capacity mode doesn't transfer. The new table starts with whatever mode you set, not the source's. Estimate the write load before a scan+write copy — size a representative item with the item-size calculator and multiply by item count to ballpark WCUs.
- TTL, streams, auto-scaling, and tags are table settings, not data — none of the copy methods carry all of them. Re-apply them on the target.
- DynamoDB JSON ≠ plain JSON. Exports and scans emit type-tagged DynamoDB-JSON; if you're transforming en route, the DynamoDB JSON converter handles the marshalling.
-
Verify before cutover. Compare item counts and spot-check records on both sides —
remembering that
DescribeTable's count is up to six hours stale, so a fresh target can legitimately report zero.
FAQ
Is there an AWS CLI command to copy a DynamoDB table?
No. There's no native copy-table command. You combine scan + batch-write-item,
or use the managed export/import or AWS Backup features.
How do I copy a DynamoDB table to another account?
Three options: a scan+write script with credentials for both accounts, an S3
export/import (the bucket can be cross-account), or AWS Backup copy+restore (both accounts
must be in the same AWS Organization).
How do I copy a DynamoDB table to another region?
S3 export/import and AWS Backup both support cross-region. For ongoing cross-region
sync rather than a one-time copy, add a global-table replica in the target region.
Does copying a table copy its indexes?
S3 import and AWS Backup restore let you keep/define secondary indexes; a scan+write
script does not — you create indexes on the target yourself, and they backfill separately.
Can I import into an existing DynamoDB table?
No. DynamoDB's S3 import and AWS Backup restore both create a new table. To merge
into an existing table, use a scan+write script.
Can I copy only part of a table?
The managed export/import and Backup paths are full-table only. For a subset, export
the filter match from DynoTable (lossless marshalled DynamoDB-JSON) or
script a filtered scan, then write the subset to the target.
A GUI makes the cutover sane: pull a lossless export of exactly the items you're
moving, then browse both source and target side by side, verify item counts and a few
sample records after the copy, and run ad-hoc checks without writing a
scan script. Download DynoTable to run the export-and-verify half of a
migration across accounts and regions.
Top comments (0)