Skip to content

How to Deploy copyparty on a VPS

Updated Oct 2026

verified on Ubuntu 26.04 · Oct 2026
We earn commissions when you shop through the links below. Full disclosure →

Self-host copyparty on a small VPS — the single-file Python file server with uploads, WebDAV and a media-aware web UI, run in Docker Compose behind Caddy HTTPS with a restore-tested backup and a pinned-image upgrade path.

Before you start
  • A VPS with 512 MB+ RAM (copyparty's own floor is 256 MB; the extra room is for Docker and the reverse proxy)
  • A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access
  • A domain or subdomain you can point at the server
Need a box for this guide? Kamatera's free tier lets you spin one up now.Start free on Kamatera → (opens in new tab)

What copyparty is

copyparty is a portable file server: you point it at a folder and it serves that folder over a web UI, with resumable uploads, deduplication, thumbnails, an audio player, a text and image viewer, and WebDAV, FTP and SFTP-style access for clients that prefer them. It is written in Python, MIT-licensed and rated 1 / 5 to deploy. If Nextcloud is more platform than you want and you just need "a place to put files that I and a few other people can reach from a browser", this is a strong fit.

This guide runs the official Docker image with Docker Compose, with Caddy in front for automatic HTTPS. Only one account exists, an administrator, and nothing is readable without logging in, which is the safe default for a server on the public internet. It picks up from "you have a server" and ends with the file server on HTTPS, a restore-tested backup and an upgrade routine. For help choosing a provider and tier, see Best VPS for self-hosting.

Server sizing

copyparty is one of the lightest apps in the catalog. On our test box the container idled at roughly 37 MB of RAM and the image plus a fresh share used about 135 MB of disk. Docker and Caddy add a little on top, so a 512 MB VPS is enough for personal use and 1 GB is comfortable.

What really sizes the server is the data, not the app. Disk is the number to plan: your share lives on the VPS, so pick a plan with the storage you need, or attach block storage later. Thumbnailing large photo or video libraries is the only thing that makes copyparty CPU- or RAM-hungry.

Prepare the server

Update the system and install the few tools this guide uses:

sudo apt-get update && sudo apt-get -y upgrade
sudo apt-get install -y curl openssl ufw dnsutils

If you're logged in as root, create a non-root sudo user and continue as that user:

adduser deploy
usermod -aG sudo deploy

Then open only SSH and the two ports the reverse proxy needs:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable

The file server will be reachable only through the reverse proxy. That matters on a Docker host: a published container port goes around ufw entirely, so the Compose file below binds copyparty to 127.0.0.1, and Caddy becomes the only public door.

Where to host itaffiliate disclosure
Hetzner Cloudrun it on
2 vCPU · 4 GB RAM · 80 GB SSD · $23.59/mo
Get Hetzner Cloud (opens in new tab)
Kamaterafree trial
1 vCPU · 1 GB RAM · 20 GB SSD · $4.00/mo
Start free on Kamatera → (opens in new tab)
DigitalOceanalso works on
1 vCPU · 1 GB RAM · 25 GB SSD · $6.00/mo
Deploy on DigitalOcean → (opens in new tab)

Paid link — we earn a commission if you shop through it.

Install Docker

copyparty is installed with Docker Compose. If Docker isn't on the server yet, the convenience script installs Docker Engine and the Compose plugin (the Docker and Compose on Ubuntu guide covers the details):

command -v docker >/dev/null || curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker "$USER"
sudo docker compose version

The group change applies on your next login; until then the commands below use sudo docker.

Install copyparty

Create the project directory and the folder to share, and generate the admin password into a .env file that Compose reads automatically. The container runs as user ID 1000, so that user must own the share. The [ -f .env ] guard keeps the step safe to re-run, so a second pass never silently changes the password under you. Set your hostname first (this guide uses files.example.com; replace it with yours):

mkdir -p ~/copyparty/share && cd ~/copyparty
sudo chown 1000:1000 share
DOMAIN=files.example.com
[ -f .env ] || printf 'CP_PASS=%s\nDOMAIN=%s\n' "$(openssl rand -hex 12)" "$DOMAIN" > .env
grep -c CP_PASS .env

Now the Compose file. It runs the official ac image (the variant with the common optional features included) with the tag pinned, so an upgrade is a deliberate edit and not a surprise. The two arguments after the image are the whole configuration: -a defines the admin account, and -v /w::A,admin shares the folder with that account alone, with full access. Caddy gets its hostname from the same .env:

cd ~/copyparty
cat > docker-compose.yml <<'EOF'
services:
  copyparty:
    image: copyparty/ac:1.20.25
    restart: unless-stopped
    user: "1000:1000"
    ports: ["127.0.0.1:3923:3923"]
    volumes: ["./share:/w"]
    command: ["-a", "admin:${CP_PASS}", "-v", "/w::A,admin"]
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports: ["80:80", "443:443"]
    environment:
      DOMAIN: ${DOMAIN}
    volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro", "caddy_data:/data"]
    depends_on: [copyparty]
volumes:
  caddy_data:
EOF
cat > Caddyfile <<'EOF'
{$DOMAIN} {
    reverse_proxy copyparty:3923
}
EOF

Start it and wait for the web UI to answer:

cd ~/copyparty
sudo docker compose up -d
for i in $(seq 1 30); do
  code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3923/)
  [ "$code" != "000" ] && break
  sleep 2
done
echo "copyparty answered HTTP $code"
sudo docker compose ps

An HTTP 200 here is the login page, not your files: the share itself is refused to anyone who isn't logged in, as the upload test below shows. Show the admin password once so you can log in:

grep CP_PASS ~/copyparty/.env

HTTPS + domain

Create an A record for your file server's hostname (say files.example.com) pointing at the server's public IP, and confirm it resolves before Caddy tries to get a certificate:

dig +short files.example.com

Caddy is already running from the previous step. It requests the certificate as soon as the name resolves, and renews it on its own (see Automatic HTTPS with Caddy for how that works). Check that HTTPS is up and the login still protects the share:

. ~/copyparty/.env
sleep 15
curl -s -o /dev/null -w 'HTTPS status %{http_code}, certificate check %{ssl_verify_result}\n' \
  "https://$DOMAIN/"

A ssl_verify_result of 0 means the certificate verified. Open https://files.example.com in a browser and confirm the padlock.

First login and hardening

Open the site and log in as admin with the password from .env. Then:

  1. Upload a file from the browser to confirm writes work, and check it lands in ~/copyparty/share on the server.
  2. Keep the single-account setup until you need more. To add people, add further -a user:password arguments and extra -v lines to the command in the Compose file, each with only the permissions that person needs (r read, w upload, A everything). Edit, then sudo docker compose up -d.
  3. Keep the share's permissions narrow. Never give a public, anonymous account write access on an internet-facing server: open uploads get abused within days.
  4. The .env file holds the password in plain text. Keep it readable only by you (chmod 600 ~/copyparty/.env).

You can also use the same credentials from a command line. This uploads and fetches a file through the HTTPS front door, then confirms that an anonymous download of the same file is refused:

cd ~/copyparty
. ./.env
echo "hello copyparty" > /tmp/hello.txt
curl -s -o /dev/null -w 'upload HTTP %{http_code}\n' -u "admin:$CP_PASS" \
  -T /tmp/hello.txt "https://$DOMAIN/hello.txt"
curl -s -u "admin:$CP_PASS" "https://$DOMAIN/hello.txt"
curl -s -o /dev/null -w 'anonymous download HTTP %{http_code}\n' "https://$DOMAIN/hello.txt"

Backups

Everything copyparty serves is in the share folder, and its settings are in the Compose file and .env. There is no database to dump: the file index is rebuilt from the folder. Archive the data and the configuration. The configuration archive contains .env, and with it the admin password, so the command also restricts both archives to their owner:

cd ~/copyparty
sudo tar czf ~/copyparty-share-$(date +%F).tar.gz share
sudo tar czf ~/copyparty-config-$(date +%F).tar.gz docker-compose.yml Caddyfile .env
sudo chmod 600 ~/copyparty-*.tar.gz
ls -lh ~/copyparty-*.tar.gz

Copy both archives off the server, to object storage, another machine, anywhere that survives this VPS dying, and run the pair from cron. For a large share, an incremental tool such as rsync or restic is a better fit than re-archiving everything nightly. To prove the archive restores, unpack it into a scratch folder and compare:

mkdir -p /tmp/cp-restore && cd /tmp/cp-restore
sudo tar xzf ~/copyparty-share-$(date +%F).tar.gz
sudo diff -r /tmp/cp-restore/share ~/copyparty/share && echo "restore matches"
sudo rm -rf /tmp/cp-restore

On a new server, install Docker, unpack the config archive into ~/copyparty, unpack the share archive beside it, make sure user ID 1000 owns share, and run sudo docker compose up -d.

Upgrades

Because the image tag is pinned, an upgrade is an edit. Check copyparty's release notes, take a backup (previous section), change the tag in docker-compose.yml, then pull and recreate:

cd ~/copyparty
sudo docker compose pull
sudo docker compose up -d
sudo docker compose logs --tail 20 copyparty

copyparty is a fast-moving project with frequent releases. There is no database migration to fear, so rolling back is just putting the previous tag back. Skim the release notes for changed command-line options before you jump several versions, since your whole configuration is command-line arguments.

Troubleshooting

Downloads return 403. That is the point: the only share requires the admin account. Log in at the web UI, or pass -u admin:PASSWORD to curl. If the correct password is rejected, check that .env still holds the one you were shown and run sudo docker compose up -d again, since the container reads it at start.

Uploads fail with "permission denied" in the logs. The share folder must be owned by user ID 1000, the user the container runs as: sudo chown -R 1000:1000 ~/copyparty/share.

Caddy never gets a certificate. Confirm dig +short files.example.com returns the server's IP and that your provider's network-level firewall, not just ufw, allows inbound 80 and 443. A blocked port 80 silently breaks the ACME challenge. Read Caddy's side with sudo docker compose logs caddy.

Large uploads stall or restart. copyparty uploads in resumable chunks, so an interrupted browser upload continues where it stopped. If it keeps failing through the proxy, test directly over an SSH tunnel to port 3923 to separate a proxy problem from a server problem.

Verification + next steps

Not exercised in our test run: the new-user step and the DNS check against your real domain (we ran on a throwaway box with a temporary hostname); a reboot of the server, so the restart-after-reboot check below is expected behaviour from restart: unless-stopped, not something we observed.

You're done when https://files.example.com loads with a valid certificate, anonymous downloads are refused, you can log in as admin and upload and download a file, docker compose ps shows both services up after a reboot (restart: unless-stopped brings them back), and you have restored a backup into a scratch folder at least once.

From there, add per-person accounts and shares, try the WebDAV endpoint from your file manager, and set up a scheduled off-server copy of the archives. For the ranked server picks and the app's catalog details, see Best VPS for self-hosting and the copyparty entry.

Next steps

How to self-host copyparty →More self-hosted file sharing tools →Automatic HTTPS with Caddy →Run Claude Code with Ollama on Your Own VPS →Deploy Coolify on a VPS →How to Deploy Actual Budget on a VPS →How to Deploy AFFiNE on a VPS →How to Deploy AnythingLLM on a VPS →How to Deploy Appwrite on a VPS →How to Deploy Audiobookshelf on a VPS →How to Deploy Authelia on a VPS →How to Deploy authentik on a VPS →How to Deploy Baserow on a VPS →How to Deploy Beszel on a VPS →How to Deploy Bitwarden on a VPS →How to Deploy BookStack on a VPS →How to Deploy CapRover on a VPS →How to Deploy Checkmate on a VPS →How to Deploy Directus on a VPS →How to Deploy Discourse on a VPS →How to Deploy docker-mailserver on a VPS →How to Deploy Docmost on a VPS →How to Deploy Dokku on a VPS →How to Deploy Dokploy on a VPS →How to Deploy Firefly III on a VPS →How to Deploy Flarum on a VPS →How to Deploy Forgejo on a VPS →How to Deploy Gatus on a VPS →How to Deploy Ghost on a VPS →How to Deploy Ghostfolio on a VPS →How to Deploy Gitea on a VPS →How to Deploy a GitHub Actions Self-Hosted Runner on a VPS →How to Deploy GitLab on a VPS →How to Deploy GlitchTip on a VPS →How to Deploy Grafana on a VPS →How to Deploy Graylog on a VPS →How to Deploy Headscale on a VPS →How to Deploy Healthchecks on a VPS →How to Deploy Home Assistant on a VPS →How to Deploy Immich on a VPS →How to Deploy Infisical on a VPS →How to Deploy Invoice Ninja on a VPS →How to Deploy Jan on a VPS →How to Deploy Jellyfin on a VPS →How to Deploy Kanboard on a VPS →How to Deploy Karakeep on a VPS →How to Deploy Keycloak on a VPS →How to Deploy Leantime on a VPS →How to Deploy LibreChat on a VPS →How to Deploy Linkwarden on a VPS →How to Deploy LocalAI on a VPS →How to Deploy Mailcow on a VPS →How to Deploy Mailu on a VPS →How to Deploy Matomo on a VPS →How to Deploy Mattermost on a VPS →How to Deploy Meilisearch on a VPS →How to Deploy Memos on a VPS →How to Deploy a Minecraft Server on a VPS →How to Deploy n8n on a VPS →How to Deploy Navidrome on a VPS →How to Deploy NetBird on a VPS →How to Deploy Netdata on a VPS →How to Deploy Nextcloud on a VPS →How to Deploy Next.js to a VPS →How to Deploy Nginx Proxy Manager on a VPS →How to Deploy NocoDB on a VPS →How to Deploy ntfy on a VPS →How to Deploy Odoo on a VPS →How to Deploy Ollama on a VPS →How to Deploy ONLYOFFICE Docs on a VPS →How to Deploy Open WebUI on a VPS →How to Deploy OpenHands on a VPS →How to Deploy OpenObserve on a VPS →How to Deploy OpenProject on a VPS →How to Deploy Outline on a VPS →How to Deploy Pangolin on a VPS →How to Deploy Paperless-ngx on a VPS →How to Deploy Passbolt on a VPS →How to Deploy Plane on a VPS →How to Deploy Plausible Analytics on a VPS →How to Deploy Plex Media Server on a VPS →How to Deploy Pocket ID on a VPS →How to Deploy PocketBase on a VPS →How to Deploy Prometheus on a VPS →How to Deploy Psono on a VPS →How to Deploy Radarr on a VPS →How to Deploy Rocket.Chat on a VPS →How to Deploy SigNoz on a VPS →How to Deploy Sonarr on a VPS →How to Deploy Stalwart on a VPS →How to Deploy Stirling-PDF on a VPS →How to Deploy Supabase on a VPS →How to Deploy Synapse on a VPS →How to Deploy Taiga on a VPS →How to Deploy TeamPass on a VPS →How to Deploy Tinyauth on a VPS →How to Deploy Traefik on a VPS →How to Deploy Trilium on a VPS →How to Deploy Twenty CRM on a VPS →How to Deploy Umami on a VPS →How to Deploy Uptime Kuma on a VPS →How to Deploy Vaultwarden on a VPS →How to Deploy Vikunja on a VPS →How to Deploy wg-easy on a VPS →How to Deploy Wiki.js on a VPS →How to Deploy WordPress on a VPS →How to Deploy Zabbix on a VPS →How to Deploy Zitadel on a VPS →How to Deploy Zulip on a VPS →Docker & Compose on Ubuntu 26.04 →Building AI Workflows with n8n →Install Open WebUI with Ollama →Adding AI-Powered Insights to Plausible Analytics →Building AI-Powered Apps with Supabase and pgvector →

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.