Deploy on Amazon AWS

Lightsail and EC2 setup, published costs, HTTPS, backups and operations.

v0.3.0 · Updated November 2, 2026
On this page

Overview

Recommended: one Ubuntu Lightsail instance, Docker Compose, Caddy HTTPS and a persistent SQLite volume. Conflux calls external model APIs: it does not host model weights, so no GPU instance is needed. This is a deployable configuration, not a claim that Conflux has been deployed or acceptance-tested on AWS.

Use the included deploy/aws/compose.yaml, .env.example and Caddyfile. The static promo site can be hosted separately; it cannot run the gateway API. PostgreSQL, RDS-backed persistence and multi-replica SQLite are not implemented. Do not deploy multiple independent app tasks expecting shared keys/quota state.

Cost, before free promotions

Published USD list prices checked 2026-10-24. Taxes, region variation, domain registration and usage beyond included allowances are extra. Confirm the selected region/bundle in AWS before purchasing. Mumbai and some other regions have lower included transfer; the official table lists the regional exceptions.

Scroll horizontally to compare all columns on small screens.
Option / assumptionMonthly estimateWhat it covers
Lightsail Linux public-IPv4 2 GB / 2 vCPU$1260 GB disk, advertised 3 TB transfer before regional adjustments. Suitable starting runtime for a solo user; a local production build may need more memory.
Lightsail Linux public-IPv4 4 GB / 2 vCPU$2480 GB disk, advertised 4 TB transfer before regional adjustments. Recommended for building on the host and a small team's gateway.
Retained Lightsail snapshots, assuming 20 GB billable storage$1$0.05/GB-month; actual billable snapshot storage is measured by AWS, not simply the database file size.
Route 53 hosted zone, if selected$0.50 plus queriesOptional; an existing DNS provider can avoid this line item. Domain registration is separate.
Illustrative 2 GB total with the above snapshot/DNS assumptions$13.50 plus queries/tax/domainCapacity assumptions, not a quote.
Illustrative 4 GB total with the above snapshot/DNS assumptions$25.50 plus queries/tax/domainCapacity assumptions, not a quote.

Lightsail's public-IPv4 bundle includes its allocated IPv4 charge; do not add the EC2 public-IPv4 line a second time. Unattached static IPs and other resources can have separate charges. Stopping an instance does not make retained resources free.

EC2 is useful for finer IAM/VPC/storage controls but has separate compute, EBS, IPv4 and transfer bills. Estimate 730 × instance hourly rate + disk GB × regional gp3 rate + 730 × public-IPv4 hourly rate + backups + DNS + transfer. VPC public IPv4 is $0.005/hour ($3.65 for 730 hours); the EBS pricing example for US East uses $0.08/GB-month gp3 ($1.60 for 20 GB). Fetch the actual instance hourly rate from AWS Pricing Calculator for your region; CPU credits, extra provisioned IOPS and bandwidth can add costs. An ALB, NAT gateway, RDS or Kubernetes cluster is not required for this single-node gateway. Avoid adding them by default.

AWS advertises up to $200 credits and a free plan for up to six months for eligible new accounts. Eligibility depends on signup date and selected plan; older account offers differ. Lightsail also has limited selected-bundle promotions. Neither is a permanent $0 hosting guarantee. Set AWS Budgets alerts, for example at an administrator-chosen $15 or $30 monthly threshold, and enable billing notifications. Budget alerts are notifications, not a hard shutdown or spending limit.

Provider free quotas remain separate. Hosting on AWS does not reset them or make paid models free. No paid inference route is enabled by these instructions.

1. Create the host and network

  • In Lightsail, create a Linux/Unix Ubuntu 24.04 LTS instance, x86_64, in a region near your users and permitted provider endpoints. Start with the 4 GB bundle if building on-host; use 2 GB after measuring runtime memory.
  • Attach a Lightsail static IPv4. Point ai.example.com to it using an A record. Set an AAAA record only if IPv6 actually reaches this instance.
  • In the Lightsail networking firewall, allow TCP 80 and 443 publicly; restrict SSH 22 to your current admin IP. If using IPv6, configure that firewall too. Do not open 3000, SQLite or a database port publicly. Allow outbound DNS and HTTPS for provider APIs, image downloads and certificates.
  • Use your SSH key to connect as ubuntu. Protect that key. For EC2, instead launch the same Ubuntu/x86 instance with encrypted gp3, an appropriate security group, SSH from your IP, HTTP/HTTPS inbound and HTTPS/DNS outbound. An Elastic IP avoids changes to the DNS address. Follow the same Compose steps below.

Caddy requires public DNS pointing at the host and inbound 80/443 for certificate issuance. Initially use DNS-only mode if your DNS vendor offers a proxy. It enables HTTPS automatically and flushes SSE; the app remains bound to host loopback.

2. Install Docker and transfer the source

On the host:

bash
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

Create /etc/apt/sources.list.d/docker.sources using the official Ubuntu Docker instructions. For Ubuntu 24.04 x86_64, the contents are:

text
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: noble
Components: stable
Architectures: amd64
Signed-By: /etc/apt/keyrings/docker.asc

Then:

bash
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo docker version
sudo docker compose version
mkdir -p ~/conflux

Transfer the source checkout from your workstation without local secrets/data:

bash
rsync -az --exclude='.git' --exclude='node_modules' --exclude='.next*' \
  --exclude='.cache' --exclude='.env*' --exclude='data' \
  --exclude='sites/promo/out' ./ ubuntu@YOUR_STATIC_IP:~/conflux/

Run rsync from the Conflux root, and replace the host. Example env files can also be excluded here; the configuration below can be written manually. The app image is built locally; no public Conflux registry image/repository URL is assumed. Do not transfer a development SQLite database casually while it is being written.

3. Configure the gateway and TLS

In ~/conflux, create root .env, permissions 0600:

dotenv
CONFLUX_DOMAIN=ai.example.com
ACME_EMAIL=you@example.com
CONFLUX_PUBLIC_ORIGIN=https://ai.example.com
CONFLUX_TRUST_PROXY=true
CONFLUX_BIND_ADDRESS=127.0.0.1
CONFLUX_PORT=3000
CONFLUX_UPSTREAM_TIMEOUT_MS=30000
CONFLUX_FIRST_OUTPUT_TIMEOUT_MS=30000
bash
chmod 600 .env
sudo docker compose -f compose.yaml -f deploy/aws/compose.yaml config --quiet
sudo docker compose -f compose.yaml -f deploy/aws/compose.yaml up --build -d
sudo docker compose -f compose.yaml -f deploy/aws/compose.yaml ps
curl --fail https://ai.example.com/api/ready

Do not paste real passwords/keys into command arguments or shared logs. First-run Docker startup creates encryption material in the private persistent volume; CONFLUX_ENCRYPTION_KEY may instead supply an existing 64-hex key, but never set a new key on an existing database. Create the administrator immediately in the HTTPS UI. A first deployment exposes setup until that happens: keep firewall access restricted to your IP during setup if necessary, then open 443 for your clients. An existing administrator/password is preserved on restart.

The override starts Caddy, keeps its certificates in named volumes, rotates container logs and forwards to the app over the private Compose network. It replaces forwarded client headers before CONFLUX_TRUST_PROXY is used. Base app settings retain non-root execution, read-only root, dropped capabilities and a writable /app/data volume. Keep the 30-second first-output deadline for the full Claude tool set. A 10-second deadline is optional after account/workload testing; it can reject busy models.

4. Configure providers, chains and clients

  • Sign in, add your official provider keys and discover current models.
  • Use the chain guide. Catalog Test is not generation acceptance. Send one small authorized request and inspect its real tool-result continuation.
  • Configure every Claude tier used; select only routes with appropriate tools/ vision and working account access. Do not rely on unresolved Gemini/ZenMux access.
  • Issue a dedicated gateway key with model permissions, expiry, rate limit and optional budget/IP allowlist. Upstream keys stay on the gateway.
  • Configure Claude using persistent setup, with base https://ai.example.com. OpenAI/Responses clients use https://ai.example.com/v1.
  • Check /api/ready, the stream below and Usage Logs. A health check proves the gateway process/database is ready, not that a provider has inference capacity.
bash
read -rsp 'Conflux gateway key: ' CONFLUX_API_KEY
printf '\n'
export CONFLUX_API_KEY
curl --fail-with-body -N https://ai.example.com/v1/chat/completions \
  -H "Authorization: Bearer $CONFLUX_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"model":"conflux/chat","messages":[{"role":"user","content":"Say hello."}],"max_tokens":32,"stream":true}'
unset CONFLUX_API_KEY

If streaming arrives all at once, check DNS proxy/CDN buffering and Caddy upstream logs. A small response can also finish too quickly to show visible chunks. Never turn on request-body or authorization logging to troubleshoot it.

5. Back up and restore

For a consistent Docker backup, stop only app, copy its entire data volume, then restart app. Keep Caddy running. This is a short maintenance window. Example from the root checkout (requires Python 3 on Ubuntu):

bash
mkdir -m 700 -p private-backups
sudo docker compose -f compose.yaml -f deploy/aws/compose.yaml stop app
conflux_container_id=$(sudo docker compose -f compose.yaml -f deploy/aws/compose.yaml ps -aq app)
conflux_volume=$(sudo docker inspect --format '{{range .Mounts}}{{if eq .Destination "/app/data"}}{{.Name}}{{end}}{{end}}' "$conflux_container_id")
test -n "$conflux_volume"
conflux_backup_dir=$(mktemp -d "$PWD/private-backups/backup-XXXXXXXX")
sudo docker run --rm --network none --read-only \
  --mount "type=volume,src=$conflux_volume,dst=/data,readonly" \
  --mount "type=bind,src=$conflux_backup_dir,dst=/backup" \
  alpine:3.22 tar czf /backup/conflux-data.tar.gz -C /data .
cp .env "$conflux_backup_dir/runtime.env"
sudo chmod 600 "$conflux_backup_dir/conflux-data.tar.gz"
sudo docker compose -f compose.yaml -f deploy/aws/compose.yaml start app

The archive includes DB, WAL-related files if present, and volume encryption key. runtime.env is sensitive and required if encryption was supplied via env. The archive is root-owned in this example. Pre-pull alpine:3.22 before the maintenance window, and always restart app if copying fails. Never use down -v or print the backup contents in shared logs.

Keep an encrypted off-host backup and test restoring onto a separate empty volume/instance. S3 with Block Public Access, SSE-KMS or an appropriate managed key, narrowly scoped instance-role access and retention rules is an option. KMS, storage and request charges are additional; local snapshots alone are not an off-host recovery plan. Host snapshots taken during writes can be inconsistent; stop app or use a consistent SQLite backup first.

For restore: stop app, populate an empty data volume from the archive, restore the matching env key if used, preserve file ownership for container UID/GID 1000, start a compatible release and verify integrity, admin login and one authorized provider request. Do not restore over a running volume or generate replacement encryption material. For migration of an existing installation, first create a consistent npm run backup -- /private/new-directory, transfer that private DB/key securely and restore into the empty AWS volume. Keep the source backup and original credentials; do not include them in the Docker build context.

### Restore into a new volume without overwriting the old one

From the root deployment checkout, after privately restoring the matching .env:

bash
conflux_restore_dir="$PWD/private-backups/backup-XXXXXXXX"
sudo docker compose -f compose.yaml -f deploy/aws/compose.yaml stop app
sudo docker volume create conflux-restored-data
sudo docker run --rm --network none --read-only \
  --mount type=volume,src=conflux-restored-data,dst=/data \
  --mount "type=bind,src=$conflux_restore_dir,dst=/backup,readonly" \
  alpine:3.22 sh -c 'test -z "$(ls -A /data)" && tar xzf /backup/conflux-data.tar.gz -C /data && chown -R 1000:1000 /data'

Replace the backup directory. The empty-volume check refuses an already populated restore destination. If it fails, inspect the separate restore volume before doing anything else; the original volume is retained. Add a separate deploy/aws/restore.override.yaml:

yaml
services:
  app:
    volumes:
      - restored-data:/app/data
volumes:
  restored-data:
    external: true
    name: conflux-restored-data

Then start with all three files, retaining this override for subsequent operations:

bash
sudo docker compose -f compose.yaml -f deploy/aws/compose.yaml -f deploy/aws/restore.override.yaml up -d
curl --fail https://ai.example.com/api/ready

Check administrator login and saved provider decryption before accepting traffic. The old volume remains available for recovery. If the restore used an env-supplied key, the matching original .env must be in place before startup. Do not point new code at the backup volume until the matching schema/release has been verified.

6. Upgrade, monitor and troubleshoot

Back up before upgrades. Transfer/build the reviewed source, then run the same Compose up --build -d command with the existing project path/name and volumes. docker compose logs --tail 100 app caddy is useful for startup errors; metadata must not contain request bodies. Check disk growth, memory, CPU, uptime, TLS expiry and bill alerts. Set log retention in Conflux Settings to fit your needs.

Migrations are transactional. Schema 5 requires a compatible release. Rollback needs the pre-upgrade code and matching backup, not deleting credential files. Do not change the Compose project name or move deployment directories without explicitly retaining the existing volume names. Otherwise Compose may create an empty installation alongside the old one.

  • Build killed/OOM: use 4 GB, or build an amd64 image on a stronger machine and transfer it privately; memory requirements are workload-dependent.
  • Caddy unhealthy/certificate failure: verify DNS A/AAAA, email and inbound 80/443.
  • 401: check the gateway key, expiry and permitted account; upstream keys do not work.
  • 503: inspect all requested tiers and hop errors; restarting AWS cannot grant quota.
  • Slow responses: separate TLS/network wait, provider prefill and CLI retries; use low effort and the verified chain. Cloud hosting may improve availability but cannot guarantee shorter upstream generation times.
  • Saved providers cannot decrypt: restore the matching original encryption key.
  • Expenses after stopping: retained instances/disks/IPs/snapshots can still bill.

Official sources

Accessed 2026-10-24; pricing is subject to region, account terms and change:

No AWS resources, public deployment or billing actions were created by this guide.