---
name: Transfer.zip
description: "Open-source file transfer service — WeTransfer alternative. Docker Compose + Caddy. Next.js + Node.js + MongoDB. robinkarlberg/transfer.zip-web. Quick Transfers (E2E P2P WebRTC) + Stored Transfers (tus + S3/disk)."
---

# Transfer.zip

**Open-source, self-hostable file transfer service.** Beautiful alternative to WeTransfer, Smash, and similar services. Two modes: **Quick Transfers** (end-to-end encrypted peer-to-peer via WebRTC — no file stored on server) and **Stored Transfers** (resumable uploads via tus protocol, stored on disk or S3, with expiry). Transfer requests (recipient uploads to you), custom branding, email notifications, S3 or local disk storage.

Built + maintained by **Robin Karlberg / transfer.zip team**. Hosted at [transfer.zip](https://transfer.zip).

- Upstream repo (web): <https://github.com/robinkarlberg/transfer.zip-web>
- Node server repo: <https://github.com/robinkarlberg/transfer.zip-node>
- Hosted: <https://transfer.zip>
- Self-hosting guide: <https://github.com/robinkarlberg/transfer.zip-web/blob/main/SELFHOSTING.md>

## Architecture in one minute

- **Two repos** — `transfer.zip-web` (Next.js main app + signaling server) and `transfer.zip-node` (file operations server)
- **Next.js** frontend + API + signaling WebSocket server
- **MongoDB** database (requires CPU with AVX for MongoDB 8.0+; use `mongo:4.4` for older CPUs)
- **Fastify** Node.js server in `transfer.zip-node` for file uploads/downloads
- File storage: **local disk** (default) or **S3-compatible** (configured in `server/conf.json`)
- Ports: `9001` (main web) + `9002` (signaling/WebSocket); both configurable
- **Quick Transfers**: WebRTC P2P, E2E encrypted (AES-GCM 256-bit), no file stored; both peers must be online simultaneously; >10 MB falls back to relay
- **Stored Transfers**: tus protocol (resumable, chunked); files stored with expiry date
- **`main` branch = self-hosting**; `production` branch = live transfer.zip site (may differ)

## Compatible install methods

| Infra               | Runtime                          | Notes                                                          |
| ------------------- | -------------------------------- | -------------------------------------------------------------- |
| **Docker Compose**  | upstream `docker-compose.yml`    | **Primary** — main app + MongoDB + optional worker              |
| **Built-in Caddy**  | `docker-compose.caddy.yml`       | Auto-SSL; use `./deploy-caddy.sh`                              |
| **Hosted**          | <https://transfer.zip>           | Free tier; no setup                                            |

## Inputs to collect

| Input                        | Example                          | Phase    | Notes                                                                              |
| ---------------------------- | -------------------------------- | -------- | ---------------------------------------------------------------------------------- |
| Domain                       | `files.example.com`              | URL      | `SITE_URL` + `COOKIE_DOMAIN` in `next/.env`                                        |
| MongoDB                      | auto-generated via `createenv.sh`| Storage  | Password randomized; no AVX CPU → use `mongo:4.4` in compose                       |
| S3 credentials (optional)    | bucket + access key + secret     | Storage  | For Stored Transfers with S3 backend; edit `server/conf.json` in node repo         |
| Node server URL              | `https://node.example.com`       | Config   | Public URL of `transfer.zip-node`; set in `next/conf.json`                         |
| Public key from worker       | PEM string                       | Security | Generated by `docker compose up worker`; must be copied to node server `keys/`     |
| Email (optional)             | SMTP creds                       | Notify   | For branding + recipient email notifications                                        |

## Deploy Quick Transfers only (simplest)

```bash
git clone https://github.com/robinkarlberg/transfer.zip-web.git
cd transfer.zip-web

# 1. Generate env files (random MongoDB password)
./createenv.sh

# 2. Set your domain in next/.env:
# SITE_URL=https://files.example.com
# COOKIE_DOMAIN=files.example.com

# 3. Deploy with built-in Caddy (auto-SSL)
# Edit .env: set CADDY_DOMAIN=files.example.com
./deploy-caddy.sh
```

For Quick Transfers only, skip the `worker` and `mongo` containers. Use `docker compose up -d app` and the signaling server.

## Deploy Stored Transfers (full setup)

Stored Transfers require the **node server** (`transfer.zip-node`) in addition to the main app:

```bash
# On main server — get the public key
docker compose up worker   # prints public key to logs; save it

# On node server (can be same machine or different)
git clone https://github.com/robinkarlberg/transfer.zip-node.git
cd transfer.zip-node
./createenv.sh
# Edit .env: set domain + email for Caddy auto-SSL
# Create ./keys/public.pem with the public key from main worker
# Optionally edit server/conf.json for S3 storage
./deploy-caddy.sh   # or docker compose up -d

# Back on main server — set the node URL
# Edit next/conf.json: set the public URL of your node server
docker compose up -d
```

## Account creation

Account creation is not available via the UI when self-hosting:

```bash
# While the main server is running:
./create-account.sh
```

## Data & config layout

- `next/.env` — site URL, cookie domain, MongoDB URI, app secrets
- `next/conf.json` — node server URL + branding S3 config
- MongoDB volume — user accounts, transfer metadata, file references
- File storage: node server disk (`/uploads`) or S3 bucket

## Backup

```bash
# MongoDB dump
docker compose exec mongo mongodump --archive > backup-$(date +%F).archive

# Files (if disk storage)
sudo tar czf files-$(date +%F).tgz /path/to/node/uploads/
```

## Upgrade

```bash
git pull && docker compose pull && docker compose up -d --build
```

## Gotchas

- **MongoDB 8.0+ requires AVX CPU support.** Modern VPS instances have AVX; older or low-end ARM boards may not. If MongoDB fails to start, change `image: mongo` to `image: mongo:4.4` in `docker-compose.yml`.
- **Two repos for Stored Transfers.** Quick Transfers = main repo only. Stored Transfers = main repo + `transfer.zip-node`. Read SELFHOSTING.md fully before starting if you want Stored Transfers.
- **`main` ≠ `production` branch.** The self-hosted version and the live transfer.zip site can diverge. Features on the live site may not be ported to `main` yet. Check `main` branch features specifically.
- **Account creation is CLI-only when self-hosted.** Run `./create-account.sh` from the server — there's no registration page. For a team install, create accounts for each user this way.
- **Public key hand-off between main and node server is manual.** Run `docker compose up worker` on the main server, copy the printed PEM key to `keys/public.pem` on the node server before starting it. This is a one-time setup step that's easy to forget.
- **Quick Transfers: both peers must be online simultaneously.** WebRTC P2P — if the sender closes the tab, the transfer is gone. No background retention. For "upload now, download later," use Stored Transfers.
- **Quick Transfers >10MB use relay.** Due to WebRTC speed complaints, the relay is forced above 10MB even when direct P2P is possible.
- **Firefox mobile Quick Transfer sending is broken** — upstream known issue; Safari is also buggy. Desktop Chrome/Chromium is most reliable.
- **Custom branding requires S3.** Branding assets (custom icon, background) are stored in S3 buckets. If you don't have S3, branding customization is unavailable.
- **Caddy is `0.0.0.0` by default** — firewall it if you don't want the Caddy admin API or metrics exposed publicly.
- **0-byte files stuck on Quick Transfer** — upstream known bug.

## Project health

Active Next.js + Node.js development, hosted product at transfer.zip, two-repo architecture, paid tiers on hosted. Maintained by Robin Karlberg.

## File-transfer-service-family comparison

- **Transfer.zip** — WebRTC P2P + stored, E2E on P2P, Next.js + MongoDB, self-hostable
- **PairDrop** — WebRTC P2P only (LAN + internet), Vue.js, simpler, no stored transfers
- **Pingvin Share** — PHP/Node, stored file sharing only, simpler setup, no P2P
- **FileShelter** — Go, stored file sharing, minimal
- **WeTransfer** — SaaS, polished, paid for large files; the incumbent

**Choose Transfer.zip if:** you want a self-hosted WeTransfer clone with both E2E P2P Quick Transfers and stored resumable uploads, and can manage a two-repo Docker setup.

## Links

- Web repo: <https://github.com/robinkarlberg/transfer.zip-web>
- Node repo: <https://github.com/robinkarlberg/transfer.zip-node>
- Self-hosting guide: <https://github.com/robinkarlberg/transfer.zip-web/blob/main/SELFHOSTING.md>
- Hosted: <https://transfer.zip>
- PairDrop (P2P-only alt): <https://pairdrop.net>
- Pingvin Share (stored-only alt): <https://github.com/stonith404/pingvin-share>
