Self-hosted cold email sending fleet. One shard = one VPS + one domain + 20 subdomains + 100 mailboxes. Exported to Email Bison for sending.
Hands-off after first-time setup: the deploy command takes a single --domain argument, buys the domain via Cloudflare Registrar if you don't already own it, discovers the zone, provisions everything.
1 root domain (bought or existing on Cloudflare)
└── 20 auto-generated subdomains
└── 5 mailboxes each (first.last@sub.domain, British female names)
= 100 mailboxes per shard
1 Contabo VPS (port 25 open by default, cold email supported)
└── docker-mailserver (Postfix + Dovecot + OpenDKIM + OpenDMARC)
└── hosts all 20 subdomains as virtual domains
- https://dash.cloudflare.com/profile/api-tokens → Create Token → Custom token
- Permissions:
Zone→DNS→EditZone→Zone→ReadAccount→Account Rulesets→EditAccount→Cloudflare Registrar→Edit
- Account Resources:
Include→<your account> - Zone Resources:
Include→All zones from an account→<your account> - Create and copy the token. One token, every future domain.
- Grab your Account ID from any domain's overview page (right sidebar).
- Sign up at https://contabo.com and add a payment method. You will not buy a VPS in the UI — the script does that via API.
- Go to https://new.contabo.com/account/security → API section. Copy the
Client IDandClient Secret(or regenerate if you haven't yet — that's fine, do it once, save the values). - Contabo's API uses OAuth2 password grant, so the other two values are just your portal login email and portal login password. That's it — no separate "API user" to create.
- You'll end up with four values total:
CONTABO_CLIENT_ID← from the API security pageCONTABO_CLIENT_SECRET← from the API security pageCONTABO_API_USER← your Contabo login emailCONTABO_API_PASSWORD← your Contabo login password
- Port 25 is open by default. Contabo's only technical limit is 25 emails/min, far above our volume (~0.7/min peak).
- Product ID.
.env.exampledefaults toCONTABO_PRODUCT_ID=V91(current cheapest Cloud VPS, ~4GB RAM). If you get aProduct not availableerror on deploy, Contabo's catalog has shifted — check https://contabo.com/en/vps/ for the current product lineup (IDs are in the V91–V107 range in 2025+), updateCONTABO_PRODUCT_ID=in.env, and re-run.
git clone <this-repo> && cd ColdEmailInfra
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# fill in the token, account ID, and Contabo key
ssh-keygen -t ed25519 -C "coldemail" -f ~/.ssh/id_ed25519 -N "" # if you don't have one./scripts/deploy_shard.py --domain example.co.ukThat's it. The script:
- Checks if the domain is already on Cloudflare. If not, checks availability via the CF Registrar API, shows the price, and (with your confirmation) registers it at cost.
- Generates 20 subdomains + 100 mailboxes.
- Provisions a Contabo VPS and sets PTR to
mail.<domain>. - Writes 123 DNS records (A/MX/SPF/DMARC/DKIM/redirect) via Cloudflare API.
- Installs docker-mailserver, creates mailboxes, generates 2048-bit DKIM keys, publishes them.
- Exports
shards/example.co.uk_bison.csvfor Email Bison import.
Flags:
--yes— skip the purchase confirmation prompt (use when scripting).--skip-purchase— fail fast if the domain isn't already on Cloudflare (safety for when you don't want accidental purchases).
Idempotent — re-run on any failure, it resumes from the last completed step (shards/<domain>.json tracks progress).
./scripts/verify_shard.py --domain example.co.ukChecks DNS propagation, FCrDNS alignment (PTR + HELO + forward A), SMTP banner, TLS cert. Prints manual Bison-test instructions.
After a deploy, shards/example.co.uk_bison.csv has Bison's exact headers:
Name, Email, Password, IMAP Server, IMAP Port, SMTP Server, SMTP Port, Daily Limit, SMTP Secure, IMAP Secure
Defaults: SMTP 465/SSL, IMAP 993/SSL, Daily Limit 10.
./scripts/destroy_shard.py --domain example.co.ukDeletes the VPS, removes all DNS records for that zone's subdomains, archives the state file. Does NOT un-register the domain at Cloudflare Registrar (that's a manual step — register/unregister decisions should be deliberate).
- Warmup: first two weeks post-deploy, keep Bison's warmup at 2–5/inbox/day, then ramp to 10.
- Spam complaint ceiling: Gmail/Yahoo cut off at 0.3%. List-hygiene concern, not infra.
- One-click unsubscribe: Bison must include
List-Unsubscribe+List-Unsubscribe-Post: List-Unsubscribe=One-Clickin outgoing mail. Manual header spot-check at go-live. - If a shard gets flagged: destroy it. Reputation is shard-isolated — don't try to rehabilitate.