The base php:*-fpm image creates /var/www/html owned by its own www-data (uid 82) and mode 1777, so that an image can run as an arbitrary user. This image replaces www-data with a fixed uid 1000 and copies the release in with COPY --chown — which re-owns what it copies into the directory, never the directory itself. It was left world-writable, sticky, and owned by a uid the container no longer has. fs.protected_symlinks — on by default on Ubuntu, Debian and most current distributions — then refuses to let a non-root process follow a symlink in such a directory, and .env is exactly that: the entrypoint keeps it on the storage volume so a generated APP_KEY survives container replacement, and links it into place. So every request 503'd with "ProjectSend is not configured yet" while `docker exec ... cat .env`, run as root, printed the file back perfectly (#1615). Three changes, each independently sufficient for the reported case, and deliberately so — this failure is silent and its symptom points away from its cause: - the image owns /var/www/html as the runtime user, at mode 755; - the entrypoint owns the symlink it creates, so it stays followable even if that directory's mode ever drifts back; - preflight distinguishes "no .env" from ".env is there and cannot be read", instead of reporting the second as the first and sending the operator off to create a file they already have. Verified by building the production image before and after: every request 503s beforehand, with /var/www/html at uid 82 mode 1777 and www-data denied on the symlink while root reads it; afterwards /up answers 200, the container reports healthy, and / redirects to /setup. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ProjectSend
Share files with your clients, from your own server.
ProjectSend is a self-hosted application for getting files to the people you work with. You upload what you want to send, choose exactly who can see it, and each client signs in to their own private page to download it.
No public link passed around by email, no third-party service holding your clients' documents, no per-seat pricing. It runs on your server, and the files stay there.
What it does
For the people you send to
- A private area per client, showing only what has been shared with them
- Sign in with an email address, with optional two-factor authentication
- Search, filter and sort their files; download one, several as a zip, or a whole folder
- Optional comments on a file, so questions live next to the thing they are about
- Email notifications when something new arrives, in their own language
For you
- Resumable uploads that survive a dropped connection, so large files actually arrive
- Organise with folders, categories and client groups
- Share with one client, a whole group, or publicly — and set an expiry date or a download limit
- Thumbnails and previews for images and documents
- Storage quotas per client, and custom fields for the details you need to keep on them
- A full activity log and download history: who got what, and when
For the installation
- Roles and permissions for your own team, so an uploader is not an administrator
- Sign-in the way you already work: LDAP, social sign-in, or plain email and password
- Themes for the client-facing pages and for outgoing email
- 16 languages
- A REST API with scoped tokens and generated OpenAPI docs
- Privacy controls, including GDPR-grade account erasure with a grace period
- Local disk or S3-compatible storage
Screenshots
The dashboard — what is in the installation, and what has been happening in it.
Your library — folders, categories, and who each file is shared with.
What your client sees — only their files, nothing else.
Getting started
With Docker — the quickest path, and the one we recommend.
git clone https://github.com/projectsend/projectsend.git
cd projectsend
cp .env.example .env # set PROJECTSEND_EDITION=community
docker compose up -d
The app is at http://localhost:8090, and the first thing it shows you is a setup screen that
creates your administrator account.
Before you put real files in it, read DOCKER.md — where your database and uploads actually live, how to move them onto paths you chose, and how to back them up so an upgrade can't take them with it.
Without Docker — for servers where it isn't an option, install from a release zip.
INSTALL.md covers requirements, .env, nginx, the background worker and cron,
updating and troubleshooting. You do not need Composer or npm on the server; the zip ships ready to
run.
Coming from ProjectSend Legacy?
The previous generation of ProjectSend lives on at projectsend/legacy. This is a rebuild rather than an upgrade, so moving across is an import rather than an update — install fresh, then bring your old site into it with the migration tool: accounts, clients, groups, categories, folders, files and history.
It never writes to your old install, and any run can be undone with a single command. MIGRATING-FROM-V1.md explains what comes across, the two routes (same machine, or a portable export from a server you can't reach), and the one change your clients will notice: they sign in with their email address now, using the same password.
Contributing
Bug reports, translations and pull requests are all welcome — see CONTRIBUTING.md for how to set up a development copy, what the checks are, and the contributor agreement.
Found a security issue? Please report it privately through GitHub's security advisories rather than opening a public issue.
License
Free software under the GNU General Public License v2, or (at your option) any later version — see LICENSE. Use it, study it, change it, share it.
Commercial licenses are available for organizations that cannot work under copyleft terms; LICENSING.md explains both options. Contributions require signing a CLA, for reasons set out in CONTRIBUTING.md.


