Nothing says free software like being able to run the software that you rely on using your own computer. The Cloud is just someone else’s computer.
Using Projectify on your own computer isn’t only free as in free beer. You can customize, extend, study, redistribute, and use Projectify and reclaim your digital freedom (according to the terms of the AGPL used in Projectify).
Projectify is free as in free speech.
This is a tutorial on how to install and run Projectify on a Debian 12 computer using only free software, common off the shelf packages.
Prerequisites
Familiarity with setting up servers in Debian using systemd is recommended. Even if you don’t have much experience with Debian or system: you can get far by asking questions. You are more than welcome to reach out to us.
You need the following software on your Debian machine to run Projectify:
- Debian 12 (bookworm) on
aarch64
(x86_64
architecture works as well) - PostgreSQL version
15.8
Node.js
version18.19.0
- Python version
3.11.2
- Caddy version
2.6.2
pipx
version1.1.0
- Poetry version
1.8.3
- Redis version
7.0.15
- Projectify release
2024.8.20
from August 20, 2024
This article was written Sep 13, 2024. The versions used here are the current package versions available in Debian 12.
Projectify configuration
Post-installation, we want to access Projectify on this computer’s browser using the following address:
http://localhost:8000
Furthermore, we want to configure the frontend and backend server to be available under the following addresses:
- Frontend running on
http://localhost:8001
- Backend running on
http://localhost:8002
The reverse proxy needs to redirects requests to the following backend addresses at
http://localhost:8002
:
- Backend administrator on
http://localhost:8002/admin/*
tohttp://localhost:8000/admin/*
- Backend WebSocket API on
http://localhost:8002/ws/*
tohttp://localhost:8000/ws/*
- Backend JSON API on
http://localhost:8002/*
tohttp://localhost:8000/api/*
Prerequisite installation
We need to install the required Node.js
, Python 3, pipx
, PostgreSQL, Redis,
and Caddy versions to run Projectify.
Install the required software by running the following commands in a Bash terminal session:
# Update package index
sudo apt update
# Upgrade outdated packages
sudo apt upgrade -y
sudo apt install -y nodejs npm python3 python3-dev \
postgresql libpq-dev pipx caddy redis
Furthermore, we install Poetry using pipx
:
pipx install poetry
We confirm that all tools are installed correctly by querying their versions:
nodejs --version
npm --version
python3 --version
psql --version
caddy version
redis-cli --version
pipx run poetry --version
This gives us the following output:
v18.19.0
9.2.0
Python 3.11.2
psql (PostgreSQL) 15.8 (Debian 15.8-0+deb12u1)
2.6.2
redis-cli 7.0.15
Poetry (version 1.8.3)
Setting up the Projectify user and database
We set up a projectify
user account and group to run all Projectify
processes. The user has their home in /var/local/projectify/home
:
sudo useradd --system \
--shell /usr/sbin/nologin \
--create-home \
--home-dir /var/local/projectify/home \
projectify
We want to make sure that the projectify
user can access a PostgreSQL database
with the same name and use the following commands:
sudo -u postgres --login createuser projectify
sudo -u postgres --login createdb projectify --owner=projectify
We make sure that /var/projectify
belongs to the projectify
user:
sudo chown projectify:projectify /var/local/projectify
How to retrieve the source code
We want to unpack the source code for Projectify in /opt/projectify
. Any
other directory works fine as well, as long as you adjust all paths in the
following instructions.
We download the release tar bundle from GitHub and make it available to the
projectify
user using the following commands run in the same terminal.
sudo mkdir /opt/projectify
sudo chown projectify:projectify /opt/projectify
We become the projectify
user with the following command:
# From here we run commands as the projectify user
sudo -u projectify bash
# Go into our own home directory
cd $HOME
We unpack the release bundle using the following commands:
# Make sure that you are now the projectify user
# The following command should tell you "projectify"
whoami
# Download release into a temporary folder
cd $(mktemp -d)
wget https://github.com/jwpconsulting/projectify/archive/refs/tags/2024.8.20.tar.gz
# Unpack into /opt/projectify
tar -x -f 2024.8.20.tar.gz \
-C /opt/projectify -zv \
--strip-components=1
Building the Projectify frontend
We navigate into the frontend
directory and install prerequisites:
# Assuming that we are still the `projectify` user:
cd /opt/projectify/frontend
npm clean-install
# Temporary fix for aarch64:
npm install sharp
We configure the build-time variables for the frontend:
export VITE_WS_ENDPOINT=/ws
export VITE_API_ENDPOINT=/api
export VITE_PROJECTIFY_DOMAIN=localhost
export VITE_GIT_COMMIT_DATE="$(date -Idate)"
export VITE_GIT_BRANCH_NAME=2024.8.20
export VITE_GIT_COMMIT_HASH=2024.8.20
export PROJECTIFY_FRONTEND_ADAPTER=node
export NODE_ENV=production
Finally, we build the frontend:
npm run build
The complete build is available in /opt/projectify/frontend/build
.
Building the Projectify backend
We proceed by retrieving the Projectify backend Python package dependencies and run the following commands:
cd /opt/projectify/backend
# Patch pyproject.toml to support stock Debian Python 3.11.2
sed -i -e 's/python = "~3.11.6"/python = "\^3.11.2"/' pyproject.toml
pipx run poetry lock --no-update
pipx run poetry install
The virtual environment contains the gunicorn
and celery
executable which
we need to start the Django server and background worker.
To determine the full path of gunicorn
and celery
, we run the following
command:
pipx run poetry run which gunicorn celery
While writing this tutorial, Poetry showed the following path, consisting of
the location of the virtual environment and the binary bin/gunicorn
itself.
/var/local/projectify/home/.cache/pypoetry/[...]/bin/gunicorn
/var/local/projectify/home/.cache/pypoetry/[...]/bin/celery
We verify that running gunicorn
works, even outside of the current Poetry
virtual environment:
/var/local/projectify/home/.cache/pypoetry/virtualenvs/projectify-Hx-n8LwU-py3.11/bin/gunicorn --version
gunicorn
can be invoked successfully and we see the following output:
gunicorn (version 22.0.0)
We use the full paths later to define the backend and background worker systemd service files.
We collect all static files using ./manage.py collectstatic
and store them in
/var/local/projectify/static
using the following commands:
# This settings module is used exclusively to collect static files
export DJANGO_SETTINGS_MODULE=projectify.settings.collect_static
export DJANGO_CONFIGURATION=CollectStatic
export STATIC_ROOT=/var/local/projectify/static
pipx run poetry run ./manage.py collectstatic --no-input
Once all static files have been collected correctly, you should see the following output:
WARNING:root:No DATABASE_URL environment variable set, and so no databases setup
173 static files copied to '/var/local/projectify/static', 499 post-processed.
Database migration
Before the backend can be started, it needs a database to work with. The database itself has already been created in a previous step and now we have populate the database schema.
The command to populate the schema in Django is ./manage.py migrate
, since
schema population in Django is called database migration. To run this
command correctly, a few environment variables need to be set so that it
can start up. We set these variables here using the following Bash commands:
# Ensure we are still in /opt/projectify/backend
cd /opt/projectify/backend
export STRIPE_ENDPOINT_SECRET=
export STRIPE_PRICE_OBJECT=
export STRIPE_SECRET_KEY=
export STRIPE_PUBLISHABLE_KEY=
export MAILGUN_DOMAIN=
export MAILGUN_API_KEY=
export FRONTEND_URL=http://localhost:8000
export ALLOWED_HOSTS=localhost
export REDIS_URL=redis://localhost:6379/0
export DJANGO_SETTINGS_MODULE=projectify.settings.production
export DJANGO_CONFIGURATION=Production
export DATABASE_URL=postgres://%2Frun%2Fpostgresql/projectify
export SECRET_KEY=migrate-key-do-not-use-anywhere-else
With these environment variables set, we can run the following to perform the actual migration:
pipx run poetry run ./manage.py migrate
The database migration finishes correctly and we see a long stream of “OK” messages like so:
[...]
Applying workspace.0052_alter_workspaceboard_options... OK
Applying workspace.0053_task_read_only_task_number_and_more... OK
Applying workspace.0054_rename_deadline_task_due_date... OK
Applying workspace.0055_rename_deadline_workspaceboard_due_date_and_more... OK
Applying workspace.0056_alter_label_options... OK
Applying workspace.0057_workspaceuserinvite_redeemed_when... OK
Applying workspace.0058_rename_workspaceboardsection_section_and_more... OK
Applying workspace.0059_rename_workspaceboard_project_and_more... OK
Applying workspace.0060_alter_workspaceuser_role... OK
Applying workspace.0061_rename_workspaceuser_teammember_and_more... OK
Applying workspace.0062_remove_task_unique_task_order_and_more... OK
Applying workspace.0063_alter_section_order_with_respect_to... OK
Applying workspace.0064_alter_label_color... OK
Initial user creation
To log in into Projectify and access the administration panel, we need to create a so called superuser. The easiest way to create a superuser is by using the Django shell from the terminal.
In the same Bash terminal, open a Django shell using the following command:
pipx run poetry run ./manage.py shell
Inside the Django shell, create a user by typing or pasting the following commands:
from projectify.user.services import internal
internal.user_create_superuser(
email="admin@localhost",
password=input("password> "),
)
This creates a user with email address admin@localhost
and a password
that you are prompted to enter after a prompt saying password>
. You can
adjust the email address to your liking.
Once the user is created, you should see something like the following output:
Initializing channels redis layer with non-TLS url and potentiallytransmitting queries in clear text!
Python 3.11.2 (main, Aug 26 2024, 07:20:54) [GCC 12.2.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.22.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from projectify.user.services import internal
...: internal.user_create_superuser(email="admin@localhost", password=input("
...: password> "))
...:
password> XXXXXXXXXXXXXXXXXXXX
Out[1]: <User: admin@localhost>
Leave the shell by entering exit()
and Enter or pressing Ctrl + D
.
Creating the service files
To instruct Debian and systemd to automatically run Projectify in the background, we need to create the following configuration files:
Caddyfile
for Caddy in/etc/caddy/Caddyfile
backend-env
environment variable configuration file for the backend and background worker in/var/local/projectify/backend-env
Furthermore, we create the following systemd service files in
/etc/systemd/system
:
projectify-frontend.service
systemd unit file for the Projectify frontendprojectify-backend.service
systemd unit file for the Projectify backendprojectify-celery.service
systemd unit file for the Projectify Celery background worker
Caddy
Caddy is already installed in the system and has its own systemd service file created automatically.
We want to configure Caddy to act as a reverse proxy for the frontend and
backend, rewriting URLs as needed, and write an access log to
/var/log/projectify/access.log
.
Leave the projectify
user shell that you have used to run the preceding
commands. You can do so by pressing CTRL+D
or typing exit
and pressing
Enter
. Run the below commands in a normal Bash session with your regular
Debian account.
We give it a Caddyfile
and restart Caddy. The following Bash commands, run in
a regular terminal session, write the Caddyfile
and restart Caddy:
# whoami should not say projectify anymore
# Create a log folder for Caddy
sudo mkdir /var/log/projectify
sudo chown caddy:caddy /var/log/projectify
sudo tee /etc/caddy/Caddyfile <<EOF
http://localhost:8000 {
reverse_proxy /admin/* http://localhost:8002
reverse_proxy /static/django/* http://localhost:8002
reverse_proxy /ws/* http://localhost:8002
handle_path /api/* {
reverse_proxy http://localhost:8002
}
reverse_proxy http://localhost:8001
log {
format console
output file /var/log/projectify/access.log
}
}
EOF
# Validate configuration
caddy validate --config /etc/caddy/Caddyfile
# Restart caddy
sudo systemctl restart caddy.service
If you are curious how Projectify on www.projectifyapp.com uses Caddy, please refer to the original Caddyfile in the Projectify repository.
Frontend
All files needed to run the Projectify frontend are stored in
opt/projectify/frontend/build
. We create a systemd unit file and enable and
start the Projectify frontend using the following commands:
sudo tee /etc/systemd/system/projectify-frontend.service <<EOF
[Unit]
Description=Projectify frontend
[Install]
WantedBy=multi-user.target
[Service]
ExecStart=/usr/bin/node /opt/projectify/frontend/build
User=projectify
Group=projectify
Environment=SVELTE_KIT_PORT=8001
EOF
# Check if the service file is correct
systemd-analyze verify /etc/systemd/system/projectify-frontend.service
# Enable and start the service
sudo systemctl enable projectify-frontend.service
sudo systemctl start projectify-frontend.service
Once all these commands have been run, you can verify whether the frontend started correctly by using the following command:
sudo systemctl status projectify-frontend.service
If everything was configured correctly, you should see something like the
following output after running the preceding systemctl status
command:
projectify-frontend.service - Projectify frontend
Loaded: loaded (/etc/systemd/system/projectify-frontend.service; enabled;
Active: active (running) since Thu 2024-09-12 02:23:43 PDT; 1s ago
Main PID: 61541 (node)
Tasks: 11 (limit: 4569)
Memory: 23.3M
CPU: 103ms
CGroup: /system.slice/projectify-frontend.service
\u2514\u250061541 /usr/bin/node /opt/projectify/frontend/build
Sep 12 02:23:43 debian systemd[1]: Started projectify-frontend.service - Projec
Sep 12 02:23:43 debian node[61541]: Listening on 0.0.0.0:8001
Backend
With the frontend configured, we now configure the backend. To avoid typing
environment variables and especially the secret key in the service file itself,
we first create a systemd
EnvironmentFile
compatible configuration file. The secret key is written into the environment
file and generated using openssl rand
.
sudo tee /var/local/projectify/backend-env <<EOF
STRIPE_ENDPOINT_SECRET=
STRIPE_PRICE_OBJECT=
STRIPE_SECRET_KEY=
STRIPE_PUBLISHABLE_KEY=
MAILGUN_DOMAIN=
MAILGUN_API_KEY=
FRONTEND_URL=http://localhost:8000
ALLOWED_HOSTS=localhost
REDIS_URL=redis://localhost:6379/0
DJANGO_SETTINGS_MODULE=projectify.settings.production
DJANGO_CONFIGURATION=Production
DATABASE_URL=postgres://%2Frun%2Fpostgresql/projectify
SECRET_KEY=$(openssl rand -hex 32)
STATIC_ROOT=/var/local/projectify/static
EOF
# Ensure that only the projectify user can read this file
sudo chown projectify:projectify /var/local/projectify/backend-env
sudo chmod 400 /var/local/projectify/backend-env
After the environment file has been written, we can add the backend systemd service file and enable and start the backend using the following commands:
sudo tee /etc/systemd/system/projectify-backend.service <<EOF
[Unit]
Description=Projectify backend
[Install]
WantedBy=multi-user.target
[Service]
ExecStart=/var/local/projectify/home/.cache/pypoetry/virtualenvs/projectify-Hx-n8LwU-py3.11/bin/gunicorn \
--config /opt/projectify/backend/gunicorn.conf.py \
--log-config /opt/projectify/backend/gunicorn-error.log
User=projectify
Group=projectify
EnvironmentFile=/var/local/projectify/backend-env
Environment=PORT=8002
EOF
# Check the service file for errors
systemd-analyze verify /etc/systemd/system/projectify-backend.service
# Enable and start the Projectify backend
sudo systemctl enable projectify-backend.service
sudo systemctl start projectify-backend.service
Finally, we check whether the Projectify backend has started correctly using the following command:
sudo systemctl status projectify-backend.service
If everything worked well, you should see something like the following output:
\u25cf projectify-backend.service - Projectify backend
Loaded: loaded (/etc/systemd/system/projectify-backend.service; disabled; preset: enabled)
Active: active (running) since Thu 2024-09-12 02:37:24 PDT; 2s ago
Main PID: 63285 (gunicorn)
Tasks: 2 (limit: 4569)
Memory: 116.0M
CPU: 529ms
CGroup: /system.slice/projectify-backend.service
\u251c\u250063285 /var/local/projectify/home/.cache/pypoetry/virtualenvs/projectify-Hx-n8LwU-py3.11/bin/python>
\u2514\u250063286 /var/local/projectify/home/.cache/pypoetry/virtualenvs/projectify-Hx-n8LwU-py3.11/bin/python>
Sep 12 02:37:24 debian systemd[1]: Started projectify-backend.service - Projectify backend.
Sep 12 02:37:24 debian gunicorn[63285]: INFO [glogging] ~ Starting gunicorn 22.0.0
Sep 12 02:37:24 debian gunicorn[63285]: INFO [glogging] ~ Listening at: http://0.0.0.0:8002 (63285)
Sep 12 02:37:24 debian gunicorn[63285]: INFO [glogging] ~ Using worker: uvicorn.workers.UvicornWorker
Sep 12 02:37:24 debian gunicorn[63286]: INFO [glogging] ~ Booting worker with pid: 63286
Sep 12 02:37:24 debian gunicorn[63286]: WARNING [production] ~ Initializing channels redis layer with non-TLS url>
Sep 12 02:37:25 debian gunicorn[63286]: INFO [server] ~ Started server process [63286]
Sep 12 02:37:25 debian gunicorn[63286]: INFO [on] ~ Waiting for application startup.
Sep 12 02:37:25 debian gunicorn[63286]: INFO [on] ~ ASGI 'lifespan' protocol appears unsupported.
Sep 12 02:37:25 debian gunicorn[63286]: INFO [on] ~ Application startup complete.
Celery
The last systemd service to configure is the background worker. We run the
following commands to create, enable, and run the Projectify background worker
projectify-celery
:
sudo tee /etc/systemd/system/projectify-celery.service <<EOF
[Unit]
Description=Projectify celery
[Install]
WantedBy=multi-user.target
[Service]
ExecStart=/var/local/projectify/home/.cache/pypoetry/virtualenvs/projectify-Hx-n8LwU-py3.11/bin/celery \
--app projectify.celery worker --concurrency 1
User=projectify
Group=projectify
EnvironmentFile=/var/local/projectify/backend-env
EOF
# Check the service file
systemd-analyze verify /etc/systemd/system/projectify-celery.service
# Enable and start projectify-celery
sudo systemctl enable projectify-celery.service
sudo systemctl start projectify-celery.service
Finally, to verify whether this step worked correctly as well, we query the
projectify-celery
service’s status using systemctl status
:
sudo systemctl status projectify-celery.service
If everything worked correctly, we see the following output:
\u25cf projectify-celery.service - Projectify celery
Loaded: loaded (/etc/systemd/system/projectify-celery.service; disabled; preset: enabled)
Active: active (running) since Thu 2024-09-12 02:39:03 PDT; 11min ago
Main PID: 63320 (celery)
Tasks: 2 (limit: 4569)
Memory: 172.5M
CPU: 2.406s
CGroup: /system.slice/projectify-celery.service
\u251c\u250063320 /var/local/projectify/home/.cache/pypoetry/virtualenvs/projectify-Hx-n8LwU-py3.11/bin/python>
\u2514\u250063322 /var/local/projectify/home/.cache/pypoetry/virtualenvs/projectify-Hx-n8LwU-py3.11/bin/python>
Sep 12 02:39:04 debian celery[63320]: - ** ---------- [config]
Sep 12 02:39:04 debian celery[63320]: - ** ---------- .> app: proj:0xffffa14ec190
Sep 12 02:39:04 debian celery[63320]: - ** ---------- .> transport: redis://localhost:6379/0
Sep 12 02:39:04 debian celery[63320]: - ** ---------- .> results:
Sep 12 02:39:04 debian celery[63320]: - *** --- * --- .> concurrency: 1 (prefork)
Sep 12 02:39:04 debian celery[63320]: -- ******* ---- .> task events: OFF (enable -E to monitor tasks in this wor>
Sep 12 02:39:04 debian celery[63320]: --- ***** -----
Sep 12 02:39:04 debian celery[63320]: -------------- [queues]
Sep 12 02:39:04 debian celery[63320]: .> celery exchange=celery(direct) key=celery
Sep 12 02:39:04 debian celery[63320]:
Logging in and configuring
Projectify is now configured and installed correctly on your Debian computer. You can access Projectify by going to the following address with your browser:
Proceed to the Log In screen by clicking the Log In button. Log in using the
credentials that you have configured when you created your user in the Django
shell before. The next screenshot shows how you can log in using the newly
created admin@localhost
email address.
Projectify then redirects you to the onboarding process where you can create your first workspace.
Go through the onboarding flow and create your first workspace.
If you would like to unlock all workspace features for your newly created workspace, you can create a coupon code in the administration panel.
To access the administration panel, go to the following address: http://localhost:8000/admin/
From there, find the coupon creation screen, which is located at http://localhost:8000/admin/corporate/coupon/add/. Using this form, you can create a coupon code.
Once you press Save, Projectify redirects you back to a list of all existing coupon codes.
Copy the resulting coupon code. It has the format XXXXX-YYYYYY
.
Go to your workspace settings by using the side navigation menu on the left.
Within the workspace settings, select the Billing tab.
Scroll down in the billing tab and enter the coupon code that you have copied after creating.
Submit the coupon code by pressing the submit button. Refresh the page with
CTRL-R
or F5
and your workspace should have all trial restrictions removed.
Conclusion
You can now use the Projectify project management app on your Debian computer. On your Debian computer, Projectify is going to work offline even without being connected to the Internet.
All steps, except for accessing the Projectify frontend from a browser, can be completed on a VPS or similar through an SSH connection as well. You can deploy and maintain Projectify on your own servers.
You can add Projectify to your company’s intranet and invite more users to use Projectify as well.
Please keep up to date with any Projectify changes and make sure to update your local environment as well. Some updates to Projectify may contain security related updates.
If you have any questions about the installation process, please reach out to us using the Projectify contact page.