Updating ProjectSend
Pull the new image and Docker takes care of the rest. What to back up, how to check the update worked, and what to do if the container does not come up.
· By ProjectSend Team

Pull the new image and let the container do the rest. For an installation using the official projectsend/projectsend image, the whole procedure is two commands: docker compose pull and docker compose up -d. The container runs the database migration before nginx accepts a single request, so there is no separate migration step. Back up the database and the storage volume first. This note is based on ProjectSend 2.7.0 (October 2026).
Before you start
Three things to do before you pull: back up the database, back up the storage volume, and note the version you are on. Migrations move forwards and cannot be reversed. The backup you take now is the one you restore from if something goes wrong.
Back up the database
docker compose exec -T db \
mysqldump -u root -p"${DB_ROOT_PASSWORD:-root}" \
--single-transaction --routines --triggers \
projectsend > projectsend-before-update.sql--single-transaction takes a consistent snapshot without locking anyone out while the dump runs. Reading the password from the environment keeps it off your shell history.
Back up the storage volume
rsync -a /srv/projectsend/storage/ /your/backup/location/storage/Back up the whole storage directory, not just the uploads inside it. storage/.env holds APP_KEY — the key that decrypts saved SMTP and LDAP passwords in the database. Back up only the uploads and you have the files without the key. If you kept the default named Docker volume instead of a bind-mounted path, the Running ProjectSend with Docker note and the Docker guide explain how to extract the volume through a throwaway container.
Note your current version
The System card on the dashboard shows the version now running. Write it down. If you need to go back after the update, the image tag for that version is what you restore to.
Pre-update checklist:
- Database dump saved outside the Docker host
storagedirectory copied, includingstorage/.env- Current version noted (System card on the dashboard)
- Changelog for the target version read — see /changelog/v2.7.0 and Version-specific steps below
Version-specific steps
Before you pull, read the changelog for the version you are moving to. Some releases include an ⚠️ Important — Do These Yourself section with steps that do not happen automatically. The update does not wait for you to take them, and ProjectSend keeps running without them — but something will behave differently until you act.
For 2.7.0, documented at the release on GitHub and at /changelog/v2.7.0, two notes apply:
- If your installation cannot send email: accounts that sign in through Google, Microsoft, or another SSO provider can no longer receive the link to set their own first password — the link now arrives by email. An administrator can set the password from the account screen until mail works.
- API tokens with Edit clients or Edit users: those tokens can now sign in as the accounts they manage. The token screen warns you when you choose either ability. Review integrations using those abilities and narrow their scope if possible.
Releases without an Important section need nothing from you beyond the update itself.
Updating the Docker image
For the official projectsend/projectsend image — started from compose.example.yaml — the whole update is two commands. Take the backups above first, then:
docker compose pull
docker compose up -dThat is the complete procedure. There is no migration step to run, no cache to clear, and nothing to do afterwards.
One thing to check first: if your compose file pins an exact tag — projectsend/projectsend:2.0.0 — pull fetches the same image you already have and the update does nothing. Change the tag to 2 (which follows every 2.x release without jumping to a major), or to the specific version you are targeting, before you pull. This is the cause behind cases where docker compose pull appears to do nothing.
If you run a compose built from a clone of the repository instead of the published image, the steps are slightly different — the image is yours to rebuild. The update guide has the full procedure for that case.
What the container does on the way up
The entrypoint runs before nginx accepts a single request. It handles the migration and does not expose the site until everything has finished, so a failed update leaves the old container gone rather than a partially-updated site serving traffic.
- Recreates any missing
storage/directories. GeneratesAPP_KEYonly if one does not already exist — yours is on the storage volume and is left alone. - Waits up to 60 seconds for the database to accept connections, so a slow-starting database container is a pause rather than a crash.
- Runs
php artisan projectsend:update, which migrates the database, restores built-in role permissions, relinks storage, clears compiled caches, and records the version applied. - Starts PHP-FPM, nginx, the queue worker, and the scheduler.
The queue worker and scheduler run inside the same container, so they are replaced when the container is replaced. They never keep running old code after an upgrade.
Checking it worked
Check the container status and the migration output:
docker compose ps
docker compose logs app | grep -A5 "Running migrations"The app container should show status healthy. The first time you sign in after the update you land on a page naming the version now running and what the release brought. After that it stays reachable from About, under the version line. The System card on the dashboard also shows the version, and the “a new version is available” notice disappears automatically once the installed version has caught up.
If the container does not come up
The entrypoint stops on the first error, so a container that restarts in a loop has already logged the reason. Read the log:
docker compose logs app | tail -50Two common causes:
- The database never became reachable. Check
DB_HOST, the credentials in your compose file, and whether the database container passed its healthcheck (docker compose ps db). - A migration could not run. The output names which one and why.
docker compose logs appgives the full history.
Neither leaves a partially-updated site serving traffic: nginx does not start until the entrypoint finishes successfully. If the container passes the entrypoint but docker compose ps shows a climbing restart count, check available memory: MySQL, Redis, and up to ten PHP-FPM workers add up on a small VPS.
Installing from the zip
If you installed from a release zip rather than Docker, the update is one script:
cd /var/www/projectsend
sudo ./update.shThe script asks three questions, takes an optional backup, puts the site into maintenance mode, unpacks the release, runs the migration, reloads PHP-FPM, and brings the site back. A stale OPcache is the most common reason the site shows the old version after the script finishes — update.sh reloads PHP-FPM for you unless you passed --no-restart. If you ran the steps manually, run sudo systemctl reload php8.4-fpm yourself.
On SELinux systems, newly unpacked files can be blocked until the security context is restored. If the site returns permission errors or 500s on pages that worked before, check /var/log/audit/audit.log for SELinux denials.
The update guide covers the full procedure, the script’s options, and every documented failure mode.
Questions and corrections
If something here does not match what you see, ask in our Discord or open an issue on GitHub. The documentation lives in the application repository, so corrections are welcome as pull requests to projectsend/projectsend too. More notes on updating and running ProjectSend are in the Upgrading category.
Frequently asked questions
- What is the Docker update procedure?
- Pull the new image with docker compose pull, then docker compose up -d. The container runs the database migration itself and does not start serving traffic until it is complete. Nothing else is needed.
- What should I back up before updating ProjectSend?
- Back up the database with mysqldump (use --single-transaction), and the full storage directory including storage/.env, which holds APP_KEY. Note the version you are on from the System card on the dashboard.
- Why does the container restart in a loop after updating?
- The entrypoint stops on the first error. Read the container log with docker compose logs app to see what failed. The two most common causes are a database that never became reachable, and a migration that could not run.
- Do I need to run a database migration manually on Docker?
- No. The official Docker image runs php artisan projectsend:update automatically on every start. You do not need to run it by hand.
- Where are the version-specific manual steps for an update?
- In the changelog for the version you are moving to, under Important — Do These Yourself. For 2.7.0, see /changelog/v2.7.0 or the release on GitHub.
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.