VayaMail

Prefer a terminal?

The same engine as a free command-line tool, with no size limit. Passwords never leave your machine. Script it for a hundred mailboxes from a CSV.

npm install -g vayamail

$ vayamail migrate \
    --from office@example.com \
    --to office@example.com
Password for office@example.com (old): ********
Password for office@example.com (new): ********
ok  old  mail.example.com:993  6,415 messages  2.1 GB
ok  new  imap.hostinger.com:993  0 messages
    INBOX            4,812 / 4,812
    Sent             1,203 / 1,203
    Invoices 2025      388 /   388
Done in 14m 22s. 6,403 copied, 12 already there.

Install

The CLI is free, with no size limit. It needs Node.js 22 or newer on macOS, Linux or Windows. Check with node --version.

$ npm install -g vayamail
$ vayamail --version

Or run it without installing anything permanent:

$ npx vayamail@latest migrate --from office@example.com --to office@example.com

Update later with npm install -g vayamail@latest. Every command explains itself with --help, e.g. vayamail migrate --help.

Prefer to have an AI assistant walk you through it?

Give ChatGPT, Claude or your coding agent this prompt. It asks you the right questions, installs the tool, and never asks for your passwords in the chat.

vayamail.com/prompt.md

Quick start: move one mailbox

The usual case: same address, new host. Create the mailbox on the new host first, with its password.

  1. 1. See what would happen

    The servers are found from the address. You are asked for both passwords (typing is hidden), then shown the folder plan and how much would be copied. Nothing is written.

    $ vayamail migrate --from office@example.com --to office@example.com --dry-run
  2. 2. Copy

    The same command without --dry-run. It asks once more before it starts. The old mailbox is only read: nothing there is changed, moved or marked as read.

    $ vayamail migrate --from office@example.com --to office@example.com --report report.csv
    ...
    Done in 14m 22s. 6,403 copied, 12 already there.
      state saved to ~/.vayamail/state/3f9a1c.json  ·  sync new mail later: vayamail sync 3f9a1c

    Note the job id at the end (3f9a1c here). If the copy is interrupted (laptop asleep, network gone, Ctrl+C), run vayamail resume 3f9a1c and it continues where it stopped.

  3. 3. Switch the domain, then catch the stragglers

    Point the domain’s MX records at the new host. Mail that reached the old mailbox in the meantime is picked up by a sync, which only looks at new messages:

    $ vayamail sync 3f9a1c

    Run it again a day later if you like. Messages already copied are always skipped, so nothing is ever duplicated.

Different addresses work the same way: --from old@example.com --to new@example.net. If a server isn’t found, give it with --from-host / --to-host. The provider guides list the server names for cPanel, Plesk, Hostinger, Gmail, Outlook and others.

Passwords

Passwords are only used to log in to the two mail servers, straight from your computer. They are never sent to VayaMail and never written to disk. There are three ways to give them, tried in this order:

VAYAMAIL_FROM_PASS / VAYAMAIL_TO_PASS
Environment variables, handy in scripts and CI.
--from-pass-file / --to-pass-file
A file holding only the password (one line). Keep it private: chmod 600 old.txt.
(nothing)
A hidden prompt asks for it.

There is deliberately no --password flag: passwords typed as flags end up in your shell history.

Gmail and Outlook.com need an app password instead of your normal one; see the Gmail and Outlook guides. Microsoft 365 work accounts that only allow Microsoft sign-in aren’t supported yet.

Commands

vayamail migrate

Copy every folder from the old mailbox to the new one. Sent, Drafts, Trash and Spam are matched by their role whatever they are called; other folders are created on the new side with the same names. Dates, read/unread, flags and attachments are kept.

$ vayamail migrate --from office@example.com --to office@example.com
$ vayamail migrate --from office@example.com --from-host server123.example-host.com --insecure \
    --to office@example.com --to-host imap.example.net --since 2024-01-01 --exclude "Archive/*"

Server options

--from <address>
Old mailbox address. Its IMAP server is found automatically.
--from-host <host>
IMAP server of the old mailbox, when autodetect picks the wrong one or finds none.
--from-port <port>
Port. Default 993 with tls, 143 with starttls or none.
--from-security <mode>
tls (port 993), starttls (143) or none (unencrypted, only for servers on your own network).
--from-user <user>
Login name, if it is not the email address (some hosts use only the part before the @).
--from-pass-file <file>
Read the password from a file instead of asking for it.
--to … --to-pass-file
The same six options for the new mailbox.

Copy options

--dry-run
Connect, plan and count. Writes nothing.
--include-trash
Also copy Trash. Left out by default.
--include-junk
Also copy Spam / Junk. Left out by default.
--since <YYYY-MM-DD>
Only messages received on or after this date.
--exclude <pattern>
Skip folders that match, e.g. "Archive/*". Repeat it for more patterns.
--map <Source=Destination>
Send one folder somewhere else, e.g. "INBOX.Sent Messages=Sent". Repeatable.
--connections <1–6>
Connections per mailbox on each side. Default 2, which shared hosting tolerates well.
--insecure
Accept a certificate issued for a different name. Common on shared hosting; see Troubleshooting.
--report <file>
Write the report to report.csv or report.json: every folder, plus every message that was not copied and why.
--json
Newline-delimited JSON progress events on stdout, for scripts.
-y, --yes
Start without asking for confirmation.
--config <file>
Read servers and options from a YAML file (see Config file). Flags override the file.
--incremental
Only look at messages newer than the ones this job already copied. You rarely need it: vayamail sync <jobId> does exactly this for a saved job.

vayamail folders

Print the folder plan only: which folder goes where, and which are skipped. Takes the same server options as migrate, plus --include-trash, --include-junk, --exclude, --map and --json. Use it to get --map right before a big run.

$ vayamail folders --from office@example.com --to office@example.com

vayamail test

Log in to one server and list its folders with message counts and size. Good for checking a password or a server name.

$ vayamail test --host mail.example.com --user office@example.com
--host, --user
Server and login name.
--port, --security
Default 993 and tls.
--pass-file <file>
Read the password from a file.
--insecure, --json
As for migrate.

vayamail detect

Show which IMAP server an address uses and how it was found (known provider, Thunderbird’s database, DNS, MX records, common names). No password needed.

$ vayamail detect office@example.com

vayamail resume <jobId> · vayamail sync <jobId>

resume continues a job that stopped; sync copies only mail that arrived since the last run. Both reuse the saved servers and options, ask for the passwords again, and accept --from-pass-file, --to-pass-file, --report and --json. See Resume and sync.

vayamail bulk <file.csv>

Migrate many mailboxes from a CSV, a few at a time. See below.

vayamail import <files or folders…> --to <address>

Upload mail saved on your computer into a mailbox. See Importing saved mail.

Many mailboxes from a CSV

One line per mailbox. The header row is optional; the two host columns can be left empty to autodetect, or hold mail.example.com, mail.example.com:143 or imaps://mail.example.com:993. Quote a password that contains a comma. Lines starting with # are ignored.

Start from the CSV template: four example lines with notes, ready to fill in a spreadsheet.

# accounts.csv
src_email,src_password,dst_email,dst_password,src_host,dst_host
office@example.com,old-password,office@example.com,new-password,,
ana@example.com,"pass,with,commas",ana@example.com,new-password-2,mail.example.com,imap.example.net
$ vayamail bulk accounts.csv --concurrency 3 --report bulk-report.csv
--concurrency <n>
Mailboxes migrated at the same time. Default 3; keep it low when they all live on the same shared server.
--connections <1–6>
Connections per mailbox on each side. Default 2.
--include-trash, --include-junk, --since
As for migrate, applied to every mailbox.
--insecure
Accept certificates issued for a different name, for every server.
--report <file>
One combined report for all mailboxes (.csv or .json).
--json
JSON events, each tagged with its mailbox.

Each mailbox gets its own job id, printed next to it, for resume and sync. The CSV holds passwords in plain text: keep it private (chmod 600 accounts.csv) and delete it when you are done.

Importing saved mail

For mail that is no longer on a server: point import at the files or folders a mail app saved, and it uploads them into a mailbox, folder by folder. Run the same command again and it continues where it stopped, skipping what is already there.

$ vayamail import ~/Downloads/Takeout/Mail/All\ mail\ Including\ Spam\ and\ Trash.mbox --to office@example.com
$ vayamail import Archive.pst --to office@example.com --dry-run
$ vayamail import ~/Library/Thunderbird/Profiles/abc.default/Mail/Local\ Folders --to office@example.com
Thunderbird
The profile’s Mail/Local Folders folder, or the whole profile folder.
Apple Mail
The Name.mbox folders made by Mailbox › Export Mailbox.
Gmail
The .mbox file from Google Takeout. Each message goes to one folder, chosen from its labels.
Outlook
A .pst file (File › Open & Export › Import/Export › Export to a file) or an .ost.
Any client
A folder of .eml files; it becomes a folder with the same name.

Read, replied, starred and Thunderbird tags are kept when the files record them; otherwise messages arrive as read. import takes the --to server options and the copy options of migrate. Step-by-step exports are in the guides for Thunderbird, Apple Mail, Gmail Takeout and Outlook .pst.

Config file

Instead of long command lines, keep the settings in a YAML file and run vayamail migrate --config job.yaml. Flags on the command line win over the file. Passwords don’t belong in it: use the prompt, the environment variables, or passFile.

# job.yaml
from:
  email: office@example.com
  host: server123.example-host.com   # optional, autodetected otherwise
  insecure: true                     # certificate issued for the hosting company
to:
  email: office@example.com
  host: imap.example.net
  passFile: ./new-password.txt       # optional; also: port, user, security
options:
  since: 2024-01-01
  exclude: ["Archive/*"]
  map:
    INBOX.Sent Messages: Sent
  includeTrash: false
  includeJunk: false
  connections: 2

Resume and sync

Every run gets a short job id. The CLI remembers the job in your home folder, never the passwords:

~/.vayamail/jobs/<id>.json
Servers, options and the folder plan, so resume and sync need nothing but the passwords.
~/.vayamail/state/<id>.json
Progress per folder: what was copied, so nothing is copied twice.
VAYAMAIL_HOME
Set it to keep these files somewhere else.

Even without the state, a rerun never duplicates mail: before copying a folder, VayaMail checks what the new mailbox already holds (by Message-ID and size) and skips it. That is why running migrate twice is safe.

Reports and exit codes

The summary at the end says how many messages were copied, already there, or not copied. --report file.csv (or .json) lists every folder and every message that was not copied with the reason, for example a message larger than the new host accepts.

exit 0
Everything was copied (or was already there).
exit 1
Done, but some messages were not copied. They are listed in the report.
exit 2
Stopped: wrong password, server not reachable, bad options.

With --json, progress comes as one JSON object per line (job:start, plan:ready, folder:start, progress, folder:done, job:done…), ready for jq or your own scripts.

$ vayamail migrate --config job.yaml --yes --json | jq -c 'select(.type == "folder:done")'

Troubleshooting

“certificate not valid for mail.example.com”
Common on shared hosting: the certificate belongs to the hosting company’s server (e.g. server123.example-host.com). When the CLI can read that name it connects with it automatically and says so. Otherwise use that server name with --from-host, or accept the mismatch with --insecure.
“no IMAP server found”
Give the server yourself with --from-host / --to-host. Your host’s help pages or the mail settings in webmail show it; vayamail test confirms it works.
Authentication failed
Check the password with vayamail test. Some hosts want only the part before the @ as the login (--from-user office). Gmail and Outlook.com need an app password.
“Too many connections” or slow progress
The server is limiting you. VayaMail waits and retries on its own; lower the pressure with --connections 1 (and --concurrency 1 in bulk).
Some messages were not copied
The report says why. Usually the message is bigger than the new host accepts, or the new mailbox is full. Everything else carries on.
Something else
Run it again with VAYAMAIL_DEBUG=1 for the full error, and write to hello@vayamail.com with the output (remove anything private).

Rather not use a terminal? The web app does the same in three steps.

Start in the browser