ar.io Logoar.io Documentation

Deploying with ARIO Deploy

ARIO Deploy is the recommended CLI tool for hosting decentralised applications on ar.io.

It streamlines the entire deployment process by uploading your build folder to Arweave, creating Arweave manifests, and automatically updating your ArNS records in a single command.

Built on the Turbo SDK, ario-deploy offers flexible payment options including pre-funded Turbo Credits or on-demand topups using ARIO or Base-ETH tokens. It works seamlessly with both Arweave and EVM wallets, making it easy to integrate permanent hosting into your existing development workflow.

New to decentralised apps?

Check out the series introduction to learn about permanent hosting and ArNS domains.

Prerequisites

Before starting, ensure you have:

  • Node.js 18+ - Download from nodejs.org
  • Upload Wallet - An Arweave JWK or EVM private key to pay for the upload
  • Solana Wallet - A base58 Solana secret key that controls your ArNS name (for ArNS record updates)
  • ArNS Name - Register one at arns.ar.io
  • Command Line Familiarity - Basic terminal/shell knowledge

Two-Key Model

ARIO Deploy uses two separate keys:

  • DEPLOY_KEY — pays for the Arweave upload (Arweave, Ethereum, Polygon, or Solana wallet)
  • ARNS_KEY — a Solana key that controls the ArNS name and signs the record update

These can be different wallets. The upload key handles payment; the ArNS key handles name ownership. If you only need to upload without updating an ArNS record, you can use the ario-deploy upload command with just a DEPLOY_KEY.

Project Setup

Let's create a new web application and configure it for deployment. ARIO Deploy works with any framework that generates a static build folder.

Initialize a new Next.js application:

Terminal
npx create-next-app@latest my-permaweb-app
cd my-permaweb-app

When prompted, select your preferences. For permanent hosting, enable static export by updating next.config.js:

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
  images: {
    unoptimized: true,
  },
  trailingSlash: true,
}

module.exports = nextConfig
  • output: 'export' generates a static site in the out folder
  • images.unoptimized: true prevents server-side image optimization
  • trailingSlash: true ensures URLs work correctly on static hosting

Add ario-deploy as a development dependency:

Terminal
npm install --save-dev @ar.io/deploy

Update your package.json to include deployment commands:

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "deploy": "next build && ario-deploy deploy --arns-name your-arns-name --deploy-folder out"
  }
}

Replace your-arns-name with your actual ArNS domain name. Note the --deploy-folder out flag is required since Next.js exports to ./out instead of the default ./dist.

Initialize a new Vite application with React:

Terminal
npm create vite@latest my-permaweb-app -- --template react
cd my-permaweb-app
npm install

Update vite.config.js to use relative paths for Arweave:

vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  base: './', // Use relative paths for Arweave
})

The base: './' setting ensures all asset paths are relative, which is required for proper loading on Arweave gateways.

Add ario-deploy as a development dependency:

Terminal
npm install --save-dev @ar.io/deploy

Update your package.json to include deployment commands:

package.json
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "deploy": "vite build && ario-deploy deploy --arns-name your-arns-name"
  }
}

Replace your-arns-name with your actual ArNS domain name.

Deploying from the Command Line

For this walkthrough, we'll deploy directly from the command line using inline credentials. For production apps, we recommend using GitHub Actions with secrets (covered later in this guide).

Dedicated Deployment Wallet

Always use a dedicated wallet for deployments to minimize security risks. Never commit wallet files or keys to version control.

ARIO Deploy uses Turbo to upload files to Arweave. Before deploying, ensure your wallet has sufficient credits.

Visit the Console app and connect your deployment wallet to view your current balance and top-up history.

Cost estimation

A typical static site (5-10 MB) costs approximately 0.1-0.5 ARIO. Credits are applied instantly and remain in your wallet for future deployments.

The npm run deploy command we configured earlier will build your app and deploy it to Arweave. You need both the upload key (DEPLOY_KEY) and the ArNS authority key (ARNS_KEY):

For Arweave upload wallets, base64 encode your JWK file. The ARNS_KEY is always a base58 Solana secret key:

Terminal
DEPLOY_KEY=$(base64 -i wallet.json) ARNS_KEY="your-base58-solana-key" npm run deploy
  • DEPLOY_KEY — your Arweave JWK wallet (base64-encoded), pays for the upload
  • ARNS_KEY — your Solana secret key (base58), signs the ArNS record update

For EVM wallets (Ethereum, Polygon, Base), use your raw private key for the upload. The ARNS_KEY is always a base58 Solana secret key:

Terminal
DEPLOY_KEY="0x1234567890abcdef..." ARNS_KEY="your-base58-solana-key" npm run deploy
  • DEPLOY_KEY — your EVM private key, pays for the upload
  • ARNS_KEY — your Solana secret key (base58), signs the ArNS record update

EVM wallet configuration

For EVM wallets, ensure your deploy script in package.json includes the --sig-type flag (e.g., --sig-type ethereum).

Interactive mode

When running from a terminal, ario-deploy will interactively prompt for any missing keys or configuration. You can omit ARNS_KEY from the command and the CLI will prompt you for it.

ARIO Deploy will:

  1. Upload files to Arweave via Turbo
  2. Create a manifest with SPA fallback detection
  3. Update ArNS records to point to the new transaction

Expected output:

Starting deployment...

✔ ARIO initialized
✔ ArNS record fetched for your-arns-name
✔ Signer created (arweave)
✔ Turbo initialized
✔ Upload planned: 12 of 48 files to upload (1.2 MiB), 36 cached, 0 duplicates
✔ Turbo credits check passed
✔ Folder uploaded: abc123def456ghi789jkl012mno345pqr678stu901v (36/48 files cached, 12 uploaded) (files cached for future uploads)
✔ ANT record updated

Deployment Successful!
┌─────────────┬───────────────────────────────────────────────┐
│ Tx ID       │ abc123def456ghi789jkl012mno345pqr678stu901v  │
│ ArNS Name   │ your-arns-name                               │
│ Undername   │ @                                             │
│ ArNS URL    │ https://your-arns-name.ar.io                  │
└─────────────┴───────────────────────────────────────────────┘

Propagation time

ArNS updates typically propagate across the gateway network within 60 seconds (the default TTL). You may need to hard refresh your browser to see changes immediately.

Reducing Upload Costs

Arweave storage is paid per byte, once. Three features keep redeploys cheap:

  • Deduplication (on by default). ario-deploy hashes every file and keeps a local cache (.ario-deploy/transaction-cache.json) of what it has already uploaded. Unchanged files reuse their existing transaction instead of being uploaded again, and files that are identical to another file in the same deploy are uploaded once. The credit check prices only what will actually be uploaded.
  • Incremental uploads (opt-in). --incremental tags each file with its content hash, and later deploys look your wallet's past uploads up on chain. A machine with no local cache, such as a fresh CI runner, still pays only for the files that changed. Uploads take a few minutes (about 5-7 in our measurements) to become findable, so a redeploy seconds after the last one may upload some files again.
  • Compression (opt-in). --compress gzip compresses each file before upload and tags it with Content-Encoding, so gateways serve it with that header and browsers decompress it transparently. HTML, JavaScript, CSS and JSON typically shrink 5-8x; already-compressed formats (JPEG, PNG, GIF, WebP, AVIF, WOFF/WOFF2, MP3, MP4, WebM, and archives) are uploaded as-is.
package.json
{
  "scripts": {
    "deploy": "next build && ario-deploy deploy --arns-name your-arns-name --deploy-folder out --incremental --compress gzip --compress-exclude \"llms*.txt,*.md\""
  }
}

--compress-exclude keeps files uncompressed that tools fetch with plain HTTP clients such as curl, which do not decode gzip unless asked to.

Measured on a 1,229-file static docs site with both flags: a redeploy from a fresh CI runner with no local cache found 1,228 files on chain and uploaded one (2.5 MiB), where the first deploy uploaded 33 MiB. Turning compression or incremental uploads on for the first time uploads each file once more, because those uploads are tracked separately.

Check a compressed deploy before relying on it

Right after a deploy, a gateway may serve a file before it has indexed that file's tags. The gateway must still send Content-Encoding in that case. The ar.io and Turbo gateways do; other operators' gateways do once they run an ar-io-node release with that fix, and until then may serve the compressed bytes without the header, which renders as garbage. Deploy to a test undername first (--undername staging) and load a few pages before pointing your main name at it.

On-Demand Payment

Instead of pre-funding Turbo Credits, you can pay for deployments on-demand. ARIO Deploy will automatically convert tokens to credits as needed.

Update your package.json to include an on-demand deployment script:

For Arweave wallets using ARIO tokens:

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "deploy": "next build && ario-deploy deploy --arns-name your-arns-name --deploy-folder out",
    "deploy:on-demand": "next build && ario-deploy deploy --arns-name your-arns-name --deploy-folder out --on-demand ario --max-token-amount 2.0"
  }
}
  • --on-demand ario enables ARIO payment mode
  • --max-token-amount 2.0 sets maximum ARIO to spend (prevents unexpected costs)

For EVM wallets using Base-ETH:

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "deploy": "next build && ario-deploy deploy --arns-name your-arns-name --deploy-folder out --sig-type ethereum",
    "deploy:on-demand": "next build && ario-deploy deploy --arns-name your-arns-name --deploy-folder out --sig-type ethereum --on-demand base-eth --max-token-amount 0.01"
  }
}
  • --sig-type ethereum required for EVM wallets
  • --on-demand base-eth enables Base Network payment
  • Wallet must be funded with ETH on Base Network

Network requirement

Base-ETH on-demand payment only works with Ethereum signer types. Your wallet must have ETH on the Base Network, not Ethereum mainnet.

For Arweave wallets using ARIO tokens:

package.json
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "deploy": "vite build && ario-deploy deploy --arns-name your-arns-name",
    "deploy:on-demand": "vite build && ario-deploy deploy --arns-name your-arns-name --on-demand ario --max-token-amount 2.0"
  }
}
  • --on-demand ario enables ARIO payment mode
  • --max-token-amount 2.0 sets maximum ARIO to spend (prevents unexpected costs)

For EVM wallets using Base-ETH:

package.json
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "deploy": "vite build && ario-deploy deploy --arns-name your-arns-name --sig-type ethereum",
    "deploy:on-demand": "vite build && ario-deploy deploy --arns-name your-arns-name --sig-type ethereum --on-demand base-eth --max-token-amount 0.01"
  }
}
  • --sig-type ethereum required for EVM wallets
  • --on-demand base-eth enables Base Network payment
  • Wallet must be funded with ETH on Base Network

Network requirement

Base-ETH on-demand payment only works with Ethereum signer types. Your wallet must have ETH on the Base Network, not Ethereum mainnet. See Base documentation for getting testnet or mainnet ETH.

Run the on-demand deployment command with both keys:

Terminal
DEPLOY_KEY=$(base64 -i wallet.json) ARNS_KEY="your-base58-solana-key" npm run deploy:on-demand

The tool will automatically convert ARIO to Turbo Credits as needed for the deployment.

Terminal
DEPLOY_KEY="0x1234567890abcdef..." ARNS_KEY="your-base58-solana-key" npm run deploy:on-demand

The tool will automatically convert Base-ETH to Turbo Credits as needed for the deployment.

The on-demand approach is ideal for:

  • Frequent deployments where pre-funding isn't convenient
  • CI/CD pipelines that need reliable automated deployments
  • Multi-team projects where different wallets handle different apps

Automating with GitHub Actions

The simplest way to automate deployments is with the official ar-io/ar-io-deploy GitHub Action. It handles node setup, dedup caching, and PR preview comments automatically.

Use incremental uploads in CI

The action stores the dedupe cache in the GitHub Actions cache, which GitHub evicts after 7 days without use, so an infrequent deploy can start with no cache and re-upload every file. Set incremental: 'true' and the action finds your wallet's past uploads on chain instead, so only the files that changed are uploaded, whatever the cache holds.

In your GitHub repository, navigate to Settings → Secrets and variables → Actions and add:

SecretDescription
DEPLOY_KEYUpload wallet key. For Arweave: base64-encoded JWK. For EVM: raw private key with 0x prefix.
ARNS_KEYBase58-encoded Solana secret key that controls the ArNS name.
ARNS_NAMEYour ArNS domain name (e.g., myapp).

Create .github/workflows/deploy.yml:

.github/workflows/deploy.yml
name: Deploy to Arweave

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install and build
        run: |
          npm ci
          npm run build

      - name: Deploy to Arweave
        uses: ar-io/ar-io-deploy@v1
        with:
          deploy-key: ${{ secrets.DEPLOY_KEY }}
          arns-key: ${{ secrets.ARNS_KEY }}
          arns-name: ${{ secrets.ARNS_NAME }}
          # Optional (see Reducing Upload Costs). incremental needs v1.2.0+ and
          # compress v1.1.0+; pin that version, since the floating v1 tag may lag.
          # incremental: 'true'
          # compress: gzip
          # compress-exclude: 'llms*.txt,*.md'

The action automatically detects and uploads the ./dist folder. For Next.js projects, add deploy-folder: ./out.

Create a separate workflow for PR preview deployments at .github/workflows/pr-preview.yml:

.github/workflows/pr-preview.yml
name: PR Preview

on:
  pull_request:
    types: [opened, synchronize, reopened, closed]

jobs:
  preview:
    runs-on: ubuntu-latest
    if: github.event.pull_request.head.repo.full_name == github.repository

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install and build
        run: |
          npm ci
          npm run build

      - name: Deploy preview
        uses: ar-io/ar-io-deploy@v1
        with:
          deploy-key: ${{ secrets.DEPLOY_KEY }}
          arns-key: ${{ secrets.ARNS_KEY }}
          arns-name: ${{ secrets.ARNS_NAME }}
          preview: 'true'
          github-token: ${{ secrets.GITHUB_TOKEN }}

When preview is enabled, the action:

  • Auto-generates an undername from the PR number (e.g., myrepo-pr-42)
  • Posts a comment on the PR with the preview URL
  • Cleans up the undername when the PR is closed

Make a commit and push to your main branch:

Terminal
git add .
git commit -m "Set up automated deployments"
git push origin main

Check the Actions tab in your GitHub repository to monitor the deployment progress.

Deployment frequency

Each push triggers a new deployment. For high-traffic repositories, consider adding paths filters to deploy only when specific files change, or use manual workflow triggers.

Summary

You now know how to deploy permanent web applications using ario-deploy:

  • Static site setup for Next.js and React + Vite with proper configuration
  • Two-key model with separate upload (DEPLOY_KEY) and ArNS authority (ARNS_KEY) keys
  • Flexible wallet options supporting Arweave, EVM, and Solana wallets
  • Payment methods including pre-funded Turbo Credits and on-demand topups with ARIO or Base-ETH
  • Lower upload costs through automatic deduplication, optional incremental uploads, and optional gzip compression
  • Command-line deployment with inline wallet credentials for quick deployments
  • GitHub Actions automation with the official ar-io/ar-io-deploy action for production deploys and PR previews

For more details, see the ARIO Deploy GitHub repository.

In the next guide, you'll learn how to use undernames to manage multiple environments and versions of your application.

How is this guide?