New deployments/copyparty/: copyparty (copyparty/ac) behind Caddy/LE for the web UI/WebDAV, plus its own SFTP (password auth) and FTPS listeners published directly. Ships update.sh, which drives container updates off copyparty's security-advisories API (api.copyparty.eu/advisories) -- policies latest|security|off. - Real client IP end-to-end: Caddy XFF/X-Real-IP + copyparty xff-src: lan. - SFTP host key + self-signed FTPS cert generated/persisted in /cfg; admin password generated on first deploy; conf auto-included via the image's % /cfg. - Firewall opens 80/443 + SFTP/FTPS + passive range (colon form for ports.d). - Wired into automations.sh, README, .gitignore; cloud-init for fresh VMs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
copyparty
copyparty — a portable file server with
resumable uploads, dedup, WebDAV, SFTP, FTPS, media indexing and
thumbnails — behind Caddy with automatic Let's Encrypt TLS. Ships with a
security-notices-aware updater (update.sh).
The web UI / WebDAV is fronted by Caddy on 443; SFTP and FTPS are served directly by copyparty (Caddy is HTTP-only and not in that path).
Required .env values
| Variable | Notes |
|---|---|
COPYPARTY_DOMAIN |
Public hostname for the web UI (e.g. files.example.com). |
ACME_EMAIL |
Let's Encrypt registration email. |
DATA_DIR |
Host folder shared as the data root (mounted at /w). Point at a data disk. |
COPYPARTY_IMAGE / COPYPARTY_TAG |
Image edition (copyparty/ac) + pinned version. |
SFTP_PORT / FTPS_PORT / FTP_PASV_RANGE |
Direct-listener ports (3922 / 3990 / 12000-12099). |
FTP_NAT |
This host's public IP, for passive FTPS through NAT (blank if direct). |
UPDATE_POLICY |
latest (default) / security / off — see Updates. |
See .env.example for the rest. Accounts and shares live in
copyparty.conf.example (deploy.sh generates the real
cfg/copyparty.conf with a random admin password on first run).
Deploy
./automations.sh # Deploy on this host → deploy: copyparty
Or build + run the self-contained artifact:
./build.sh
scp deploy.sh root@host:
ssh root@host 'bash deploy.sh'
# non-interactive:
# COPYPARTY_DOMAIN=files.example.com ACME_EMAIL=me@example.com SKIP_PROMPTS=1 bash deploy.sh
Unattended provisioning: cloud-init.yml. On first run deploy.sh
pins COPYPARTY_TAG to the newest release, generates cfg/copyparty.conf (random
admin password, printed once) and a self-signed cfg/ftps.pem, opens the ports,
brings the stack up, and schedules update.sh.
Accounts & shares
The default config is private: one admin account with full access to the
whole data root, nothing public. Edit cfg/copyparty.conf (commented examples
included) to add users or open read-only / anonymous-upload shares, then hot-reload:
docker compose exec copyparty kill -s USR1 1
Real client IP (behind Caddy)
For the web UI, copyparty must see the genuine client IP (for its logs and abuse bans), which means both sides are configured:
- Caddy is the internet edge, so it appends the real address to
X-Forwarded-Forand also setsX-Real-IP(seeCaddyfile). - copyparty trusts that upstream via
xff-src: lanincopyparty.conf.example— without it copyparty ignores the header and logs/bans the Docker-network address.
lan (trust private ranges) is safe here because copyparty's HTTP port is never
published — only Caddy can reach it. If copyparty prints an xff-src warning at
startup, narrow it to the exact subnet it names. SFTP/FTPS are direct TCP, so
they already see the real client IP (no XFF involved).
SFTP & FTPS
Both authenticate against the same [accounts] as the web UI.
sftp -P 3922 admin@files.example.com # SFTP (password auth enabled)
lftp -u admin -e 'set ftp:ssl-force true; set ssl:verify-certificate no' \
ftp://files.example.com:3990 # explicit-TLS FTPS
-
SFTP host key is generated on first start and persisted under
/cfg, so clients don't get "host key changed" warnings across restarts. -
FTPS cert defaults to the self-signed
cfg/ftps.pem(clients must accept it). To serve a publicly-trusted cert, reuse Caddy's Let's Encrypt cert: mount thecaddy-datavolume read-only into the copyparty service and point copyparty at it, e.g.# docker-compose.yml, copyparty service: volumes: - caddy-data:/caddy:ro# cfg/copyparty.conf [global]: cert: /caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/files.example.com/files.example.com.crt certkey: /caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/files.example.com/files.example.com.key(copyparty re-reads the cert on
kill -s USR1; refresh after a renewal.) -
Passive FTPS needs the
FTP_PASV_RANGEports published (they are) andFTP_NATset to the server's public IP when clients connect through NAT. -
A published Docker port bypasses the host
INPUTfirewall, so setBIND_ADDRto pin the listeners to a trusted interface on multi-homed hosts.
Updates & security notices
copyparty doesn't self-update, but it publishes a machine-readable
security-advisories feed (the same one its built-in --vc-url check uses).
This deployment uses it on two levels:
- In-app —
vc-url+vc-ageincfg/copyparty.confmake copyparty itself check whether the running version has a known advisory and warn in the log / control panel. (vc-exit, opt-in, makes it shut down if vulnerable.) update.sh— reads the same feed to act, pinning the new version in.envand recreating the container (with health-checked rollback).
./update.sh check # report current vs latest + any advisory; change nothing
./update.sh update # update now (TARGET_VERSION=1.20.11 to pin a version)
./update.sh install # (re)schedule the daily run / uninstall to stop
UPDATE_POLICY (in .env or /etc/copyparty-update.conf) controls the
scheduled run:
| Policy | Behaviour |
|---|---|
latest (default) |
Update to the newest release whenever one exists. |
security |
Update only when the running version has a known advisory, to the patched release named in the feed. |
off |
Never change the running version (check/notify only). |
VC_FEED selects the feed (advisories-panic / advisories / advisories-all).
Update results reuse the ntfy config at /etc/ssh-notify.conf (same as the SSH
login notifier / host auto-update), when present.
Files
| File | Purpose |
|---|---|
docker-compose.yml |
caddy + copyparty; publishes SFTP/FTPS/passive ports. |
Caddyfile |
TLS + reverse proxy for the web UI/WebDAV (real-IP headers). |
copyparty.conf.example |
Config template (globals, security check, SFTP/FTPS, accounts, volumes). |
update.sh |
Security-notices-aware container updater. |
.env.example |
Stack tunables. |
deploy.sh / build.sh |
Self-contained installer + archive embedder. |
cloud-init.yml |
Fresh-VM bootstrap (harden SSH, then deploy). |
Notes
- No Anubis PoW gate — it would break WebDAV / API / upload clients.
- The web UI/WebDAV go through Caddy; SFTP/FTPS bypass it by design.
- DNS for
COPYPARTY_DOMAINmust resolve to the host and 80/443 be reachable before deploy for the LE cert to issue.