Deployment & phone testing

Run CrossUpload with Docker Compose, configure S3 and CloudFront, and test with a real phone on your LAN.

Local: one command

git clone … crossupload && cd crossupload
docker compose up

This starts the Rust app (port 8080, download origin 8081), PostgreSQL, a MinIO-compatible object store (9000) and ClamAV. Migrations, buckets, versioning and CORS are initialized automatically. No .env is needed. ClamAV downloads signatures on first start and can take a few minutes to become ready; files wait in “Checking file…” until it is.

Testing with a real phone

localhost on a phone means the phone. To test QR uploads from a real device, both the mobile page and the storage endpoint must be reachable from the phone:

# .env (replace with your computer's LAN IP)
PUBLIC_BASE_URL=http://192.168.1.20:8080
MOBILE_BASE_URL=http://192.168.1.20:8080
DOWNLOAD_BASE_URL=http://192.168.1.20:8081
STORAGE_ENDPOINT_PUBLIC=http://192.168.1.20:9000

Put the phone on the same Wi-Fi, run docker compose up again and open /demo on the computer using the LAN address. Some phone features (camera capture, clipboard) require HTTPS; for those, use a tunnel or a local TLS proxy and set the URLs to the HTTPS addresses.

Production

  • Use compose.prod.yaml with the bundled Caddy reverse proxy (HTTPS, SSE without buffering). PostgreSQL and storage admin ports are not published.
  • The public site is static: a one-shot site service runs crossupload export-static /site on each deploy, and Caddy serves those files directly. Login, dashboard, API, SSE, the phone page and the SDK are proxied to the app.
  • Set APP_ENV=production. The app refuses to start with development passwords, the default signing key, disabled scanning, the log email sink, private webhook destinations or non-HTTPS URLs.
  • Storage: Amazon S3 with two private buckets (quarantine and delivery), versioning on, Block Public Access, SSE encryption. Credentials come from the AWS provider chain (instance role recommended).
  • Delivery: CloudFront with Origin Access Control to the delivery bucket only, and a trusted key group for signed URLs (DELIVERY_MODE=cloudfront).
  • Billing: Stripe keys and three price IDs; point a Stripe webhook at /webhooks/stripe.
  • Email: an SMTP URL for password resets.

Example IAM, bucket, CORS and CloudFront configuration lives in deploy/aws/.

Backups

docker compose exec -T postgres pg_dump -U crossupload -Fc crossupload > backup.dump
docker compose exec -T postgres pg_restore -U crossupload -d crossupload --clean < backup.dump

Back up the object store volume (or rely on S3 versioning and replication in production). A single-host deployment is a single point of failure; it is not highly available.


Something unclear or wrong? Email master@crossupload.com.