Moving from ProjectSend Legacy to ProjectSend 2
An overview of the migration tool: what it brings over, what it leaves behind, and the steps for the most common setup.
· By ProjectSend Team

ProjectSend 2 is a new codebase, so installs on the PHP generation (r2098 and earlier) don't upgrade in place. Instead, you set up a new ProjectSend 2 install and bring your data over with a migration tool. This post is an overview of how that works; the migration guide has every option.
One thing we wanted from the start: the tool never writes to your Legacy install. Your old site keeps working while you try the migration, check the result, undo it and run it again. On a small install the whole thing takes about twenty minutes.
Before you start
- Clients sign in with their email address. Legacy used usernames and ProjectSend 2 uses email. Passwords carry over, so nobody has to reset theirs. Since Legacy didn't require unique emails, the tool stops until duplicate, blank or invalid addresses are fixed in Legacy, and it tells you which accounts they are. We left that decision to you on purpose: when two clients share an address, only you know which one should keep access.
- The new install should be empty. Create the admin account and then migrate, before adding files or clients.
- The background worker has to be running, because the import runs as a job. In Docker it's already there; for a manual install, see the install guide.
What carries over
Settings that have an equivalent (plus your SMTP configuration), CAPTCHA keys (stored encrypted now), roles and permissions, staff and client accounts with their password hashes, quotas and custom fields, groups, categories, folders, files (the records and the files themselves), who each file was shared with, and the download and activity history.
Two small differences:
- Categories are flattened, so a nested category keeps its full path as its name, like
Clients / Acme / Invoices. - Download limits keep their count: a file limited to 3 downloads that was downloaded twice arrives with one left.
What doesn't
The preflight lists these before anything runs:
- files encrypted at rest, and files stored on S3, GCS or Azure;
- hidden assignments (recreating them would show people files that were hidden from them);
- two-factor secrets, so those users set up 2FA again;
- email templates, because the placeholders changed;
- Legacy settings with no equivalent, and thumbnails, which get regenerated.
The usual setup: Legacy on the server, ProjectSend 2 in Docker
Run these from the folder that holds your compose.yaml. First, add the tool to the new install:
docker compose exec -u www-data app \
php -r 'copy("https://getcomposer.org/composer.phar", "/tmp/composer.phar");'
docker compose exec -u www-data app \
php /tmp/composer.phar require projectsend/v1-migration-tool --update-no-dev
docker compose exec -u www-data app php artisan migrate(-u www-data keeps vendor/ from ending up owned by root.)
Then export from Legacy on the host, using the same PHP your Legacy site runs on, and hand the result to the container:
docker compose cp \
app:/var/www/html/vendor/projectsend/v1-migration-tool/bin/projectsend-v1-export.php .
php projectsend-v1-export.php --install=/var/www/projectsend-legacy --preflight
php projectsend-v1-export.php --install=/var/www/projectsend-legacy --out=/srv/ps-export --files=copy
docker compose cp /srv/ps-export app:/tmp/ps-export
docker compose exec app chown -R www-data:www-data /tmp/ps-exportIf both installs live on the same machine and neither runs in Docker, there's a simpler direct route that reads Legacy's folder and database itself. The guide covers both.
Preflight, import and verify
docker compose exec -u www-data app php artisan projectsend:migrate:preflight --bundle=/tmp/ps-export
docker compose exec -u www-data app php artisan projectsend:migrate:import --bundle=/tmp/ps-export
docker compose exec -u www-data app php artisan projectsend:migrate:verifyPreflight doesn't change anything: it reports what it found and anything that needs fixing first. The import is checkpointed, so it picks up where it left off if the worker restarts. Verify checks that every record adds up and that every file is where it should be. After that, it's worth signing in and opening a client's portal to see it for yourself.
If you want to start over:
docker compose exec -u www-data app php artisan projectsend:migrate:resetThe one exception is --files=move, which takes the files out of Legacy; the command warns you before doing anything.
Switching over
- Let your clients know they'll sign in with their email address from now on, with the same password. The run report lists who's affected, including anyone who may need to use Forgot password once.
- Keep Legacy around read-only for a while, as a reference.
- Remove the tool when you're done. If you might want to redirect old
download.php?id=…links later, keep its id map by skipping--drop.
Questions
The migration guide covers large installs, the linuxserver/projectsend container and troubleshooting. If you get stuck, or find something that doesn't match what you see, let us know on Discord or in a GitHub issue.
Frequently asked questions
- Can I upgrade ProjectSend Legacy to ProjectSend 2 in place?
- No. ProjectSend 2 is a new codebase, so you set up a new install and bring your data over with the projectsend/v1-migration-tool package.
- Does the migration change my Legacy install?
- No. The tool never writes to Legacy, so you can try the migration, undo it with projectsend:migrate:reset and run it again.
- Do my clients need new passwords?
- No. Passwords carry over. What changes is that clients sign in with their email address instead of a username.
- What doesn't come across?
- Files encrypted at rest, files stored on S3, GCS or Azure, hidden assignments, two-factor secrets, email templates, Legacy settings with no equivalent, and thumbnails, which get regenerated.
ProjectSend Team. Written by the people who build and maintain ProjectSend, the open-source client portal developed in the open since 2011.
ProjectSend is free and open source
It's GPL v2 and built in the open. If something here didn't work for you, or you know a better way to do it, let us know: that's how the docs and the software get better.
Prefer not to run a server? ProjectSend Cloud is the hosted version, run by the same people.