ar.io Logoar.io Documentation
ARIO Deploy

Deduplication

By default, ario-deploy caches your deployment log to prevent uploading duplicate (unchanged) files. This saves both time and upload costs by reusing existing data on Arweave.

How it works:

  1. When you deploy, ario-deploy hashes each file in your build
  2. It checks the local cache for matching hashes from previous uploads
  3. Files that haven't changed are skipped - the existing transaction ID is reused
  4. Files identical to another file in the same deploy are uploaded once and share its transaction (static exports often write the same payload under several names)
  5. Only new or modified files are uploaded to Arweave, and each id is written to the cache the moment it lands, so a deploy that fails or is interrupted part-way does not pay for those files again
  6. The cache is stored locally in .ario-deploy/transaction-cache.json, with a separate file per Turbo network (--dev uploads never stand in for production ones)

Entries are keyed on the file's content and content type (plus encoding when compressed), so byte-identical files served as different types are never confused. Caches written by 1.x, keyed on the hash alone, are still honoured, except for empty files, whose hash says nothing about their type.

Symlinks inside the deploy folder are followed only while they point inside it; a link to a file outside the folder stops the deploy, since uploading it would publish that file permanently.

Files that are never uploaded

The check is built to stop a key from being published by accident. It cannot promise to find a key that someone disguises on purpose (reversed, split across files, or in an encoding of their own), so keep keys outside the project folder.

Before any request is made, deploy and upload list the files once, read every one of them and refuse to publish a private key. Only the files that were checked are uploaded. The error names the file and never prints the key. There is no flag to override this.

Keys the run holds. Every key the command was given (--wallet, --arns-wallet, --private-key, --arns-private-key, DEPLOY_KEY and ARNS_KEY, whichever are set) is searched for in every file and every file name. The search covers:

  • the key's raw bytes, hex in any case (also as 0x0c, 0x22, \x0c\x22 or 0c:22), a decimal byte list such as [12, 34, ...], base58, and base64 or base64url at any alignment
  • an Arweave key's private JWK fields, the base64 JWK that DEPLOY_KEY holds, and each key file base64-encoded, as in a data: URI
  • text with spaces, line breaks, string concatenation, \u, \x and percent escapes removed, UTF-16 text, every string of a JSON file, and printable text inside binary files

Gzip, brotli (.br), zip and tar files are opened and searched, including archives inside archives up to three levels deep. A compressed file is refused as one that could not be checked when it expands to more than 1 GiB, is nested deeper, is damaged or encrypted, or is a 7z, RAR, xz, bzip2, Zstandard or cabinet archive. The run also stops when a wallet file, or a hard link or symlink to one, is inside the deploy folder or is the --deploy-file.

Keys the run does not hold. The run also stops on what can be proved to be a private key, in files and inside the archives above:

  • an environment file: .env, .env.local, .env.example, prod.env, in any case (scripts and pages such as env.js are not refused for their name)
  • a PEM private key block (-----BEGIN PRIVATE KEY----- and the RSA, EC, DSA, OPENSSH and ENCRYPTED forms); public keys and certificates are not refused
  • a Solana keypair written as a byte array, base58 or hex, checked by deriving its public half from its seed
  • an object with a private exponent d and an RSA-sized modulus n (2048 bits or more) together, as JSON, inside a string or base64-encoded

What is not detected for a key the run does not hold: a 32-byte seed or an Ethereum key on its own in any form (it cannot be told from a hash), brotli data without a .br name, and keys inside compressed parts of other formats, such as PNG text chunks or PDF streams.

.git folders are left out of folder uploads, with a one-line note.

The Turbo credit check runs after this planning step, so it prices only what will actually be uploaded, not the whole folder.

Disable deduplication:

If you need to force a fresh upload of all files (e.g., for debugging or to ensure a completely new deployment). Files that are identical within the same deploy are still uploaded once and share a transaction, since that reuses nothing from earlier deploys:

ario-deploy deploy --wallet ./wallet.json --no-dedupe

Limit cache size:

The dedupe cache uses an LRU (Least Recently Used) eviction strategy. By default, it keeps up to 10,000 entries. You can adjust this limit:

ario-deploy deploy --wallet ./wallet.json --dedupe-cache-max-entries 1000

Cache location:

The cache files are stored in .ario-deploy/ in your project root. You can:

  • Add it to .gitignore if you don't want to share cache across team members
  • Commit it to share cached transaction IDs with your team (reduces duplicate uploads)
  • Delete it to start fresh: rm -rf .ario-deploy/

How is this guide?