Installing taiga.io on Debian Jessie

Taiga is a project management application that can handle both simple and complex projects for startups, software developers, and other target teams. It tracks the progress of a project. Taiga's design is clean and elegant design—something that is supposed to be “beautiful to look at all day long.” With Taiga, you can use either Kanban or Scrum template, or both. Backlogs are shown as a running list of all features and User Stories added to the project.[5]

Taiga integrates video conferencing functions with the use of third party services from Talky.io and Appear.in.[6] Group and private chat is done via HipChat.[7]

This documentation explains how to deploy a Taiga service (each module is part of the Taiga platform). the guide is based on the official taiga,io guide but adapted to work for Debian 8.

The Taiga platform consists of three main components:

  • taiga-back (backend/api)
  • taiga-front-dist (frontend)
  • taiga-events (websockets gateway) (optional)

And each one has its own dependencies, at compile time and runtime.

Each component can run on one unique machine or each can be installed in a different machine. In this tutorial we will setup everything in one single machine. This type of setup should suffice for small/medium production environments.

2. Before starting

This tutorial assumes that you are using a clean, recently updated, Debian 8 image.

Due the nature of frontend application, you should know that this service will be used through the domain/public-ip. This is because, the frontend application will run on your browser and it should communicate with the backend/api.

Taiga installation must be done with a "normal" user, never with root.

We assume the following:

  • ip: 80.88.23.45
  • hostname: example.com (which points to 80.88.23.45)
  • username: taiga
  • system ram >=1GB (needed for compilation of lxml)

3. Backend installation

This section helps with the installation of the backend (api) Taiga service.

3.1. Install dependencies

The Backend is written mainly in python (3.4) but for some third party libraries we need to install a C compiller and development headers.

sudo apt-get install -y build-essential binutils-doc autoconf flex bison libjpeg-dev
sudo apt-get install -y libfreetype6-dev zlib1g-dev libzmq3-dev libgdbm-dev libncurses5-dev
sudo apt-get install -y automake libtool libffi-dev curl git tmux gettext

3.2. Setup a database

taiga-back also requires postgresql (>= 9.4) as a database

Install postgresql:

sudo apt-get install -y postgresql-9.4 postgresql-contrib-9.4
sudo apt-get install -y postgresql-doc-9.4 postgresql-server-dev-9.4

And setup the initial user and database:

sudo -u postgres createuser taiga
sudo -u postgres createdb taiga -O taiga

3.3. Setup python environment

For run taiga-back you should have python (3.4) installed with some other third party libraries. As a first step, start installing python and virtualenvwrapper:

sudo apt-get install -y python3 python3-pip python-dev python3-dev python-pip virtualenvwrapper
sudo apt-get install libxml2-dev libxslt-dev
Note
virtualenvwrapper helps maintain the system clean of third party libraries installed with language package manager, installing them in isolated virtual environment.

Restart the shell or run bash again, to reload the bash environment with virtualenvwrapper variables and functions.

The next step is to download the code from github and install their dependencies:

Download the code
cd ~
git clone https://github.com/taigaio/taiga-back.git taiga-back
cd taiga-back
git checkout stable
Create new virtualenv named taiga
mkvirtualenv -p /usr/bin/python3.4 taiga
Install dependencies
pip install -r requirements.txt
Populate the database with initial basic data
python manage.py migrate --noinput
python manage.py loaddata initial_user
python manage.py loaddata initial_project_templates
python manage.py loaddata initial_role
python manage.py compilemessages
python manage.py collectstatic --noinput

This creates a new user admin with password 123123.

If you want some example data, you can execute the following command, which populates the database with sample projects and random data; useful for demos:

python manage.py sample_data

And as final step for setup taiga-back, you should create the intial configuration for proper static/media files resolution and optionally, email sending support:

Put this on ~/taiga-back/settings/local.py
from .common import *

MEDIA_URL = "http://example.com/media/"
STATIC_URL = "http://example.com/static/"
ADMIN_MEDIA_PREFIX = "http://example.com/static/admin/"
SITES["front"]["scheme"] = "http"
SITES["front"]["domain"] = "example.com"

SECRET_KEY = "theveryultratopsecretkey"

DEBUG = False
TEMPLATE_DEBUG = False
PUBLIC_REGISTER_ENABLED = True

DEFAULT_FROM_EMAIL = "no-reply@example.com"
SERVER_EMAIL = DEFAULT_FROM_EMAIL

# Uncomment and populate with proper connection parameters
# for enable email sending. EMAIL_HOST_USER should end by @domain.tld
#EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
#EMAIL_USE_TLS = False
#EMAIL_HOST = "localhost"
#EMAIL_HOST_USER = ""
#EMAIL_HOST_PASSWORD = ""
#EMAIL_PORT = 25

# Uncomment and populate with proper connection parameters
# for enable github login/singin.
#GITHUB_API_CLIENT_ID = "yourgithubclientid"
#GITHUB_API_CLIENT_SECRET = "yourgithubclientsecret"

3.4. Verification

To make sure everything is working, you can run the backend in development mode with:

workon taiga
python manage.py runserver

Then you must be able to see a json representing the list of endpoints in the url http://localhost:8000/api/v1/ .

Note
At this stage the backend has been installed successfully. But you’re not done yet. Because python in production environments, should run on an application server. The details for this are explained in the final section of this document.

4. Frontend installation

Download the code from github:

Download the code
cd ~
git clone https://github.com/taigaio/taiga-front-dist.git taiga-front-dist
cd taiga-front-dist
git checkout stable

And now, you can configure it copying the ~/taiga-front-dist/dist/conf.example.json to ~/taiga-front-dist/dist/conf.json and editing it.

Copy and edit initial configuration on ~/taiga-front-dist/dist/conf.json
{
    "api": "http://example.com/api/v1/",
    "eventsUrl": "ws://example.com/events",
    "debug": "true",
    "publicRegisterEnabled": true,
    "feedbackEnabled": true,
    "privacyPolicyUrl": null,
    "termsOfServiceUrl": null,
    "maxUploadFileSize": null,
    "contribPlugins": []
}

Now, having taiga-front-dist downloaded and configured, the next step is to expose the code (in dist directory) under static file web server: we use nginx. That process is explained in the final section of this tutorial.

6. Final steps

If you are here, it’s probable that you completed the installation of taiga-back and taiga-front-dist. However, having installed them is insufficient.

taiga-back should run under an application server which in turn should be executed and monitored by a process manager. For this task we will use gunicorn and circus respectivelly.

taiga-front-dist and taiga-back should be exposed to the outside, using good proxy/static-file web server. For this purpose we’ll use nginx.

6.1. Circus and gunicorn

Circus is a process manager written by Mozilla and you will use it to execute gunicorn. Circus not only serves to execute processes, it also has utils for monitoring them, collecting logs, restarting processes if something goes wrong, and starting processes on system boot.

Install circus
sudo pip2 install circus
Create conf folder if it doesn’t exist
mkdir -p ~/conf/
Initial configuration for circus on ~/conf/circus.ini
[circus]
check_delay = 5
endpoint = tcp://127.0.0.1:5555
pubsub_endpoint = tcp://127.0.0.1:5556
statsd = true

[watcher:taiga]
working_dir = /home/taiga/taiga-back
cmd = gunicorn
args = -w 3 -t 60 --pythonpath=. -b 127.0.0.1:8001 taiga.wsgi
uid = taiga
numprocesses = 1
autostart = true
send_hup = true
stdout_stream.class = FileStream
stdout_stream.filename = /home/taiga/logs/gunicorn.stdout.log
stdout_stream.max_bytes = 10485760
stdout_stream.backup_count = 4
stderr_stream.class = FileStream
stderr_stream.filename = /home/taiga/logs/gunicorn.stderr.log
stderr_stream.max_bytes = 10485760
stderr_stream.backup_count = 4

[env:taiga]
PATH = /home/taiga/.virtualenvs/taiga/bin:$PATH
TERM=rxvt-256color
SHELL=/bin/bash
USER=taiga
LANG=en_US.UTF-8
HOME=/home/taiga
PYTHONPATH=/home/taiga/.virtualenvs/taiga/lib/python3.4/site-packages

Note

Circus stats can generate a high cpu usage without any load you can set statsd to false if you don’t need them.

Taiga stores logs on the user home, making them available and immediately accessible when you enter a machine. To make everything work, make sure you have the logs directory created.

You can create it with: mkdir -p ~/logs

Setup circus for start on boot putting this on /etc/init/circus.conf
start on filesystem and net-device-up IFACE=lo
stop on runlevel [016]

respawn
exec /usr/local/bin/circusd /home/taiga/conf/circus.ini

And finally start circus:

sudo systemct start circus 

6.2. Nginx

Nginx is used as a static file web server to serve taiga-front-dist and send proxy requests to taiga-back.

First install it:

sudo apt-get install -y nginx

And now let us start configuring it:

Add specific configuration for taiga-front-dist and taiga-back on /etc/nginx/sites-available/taiga.
server {
    listen 80 default_server;
    server_name _;
<span class="tok-kn">large_client_header_buffers</span> <span class="tok-mi">4</span> <span class="tok-mi">32k</span><span class="tok-p">;</span>
<span class="tok-kn">client_max_body_size</span> <span class="tok-s">50M</span><span class="tok-p">;</span>
<span class="tok-kn">charset</span> <span class="tok-s">utf-8</span><span class="tok-p">;</span>

<span class="tok-kn">access_log</span> <span class="tok-s">/home/taiga/logs/nginx.access.log</span><span class="tok-p">;</span>
<span class="tok-kn">error_log</span> <span class="tok-s">/home/taiga/logs/nginx.error.log</span><span class="tok-p">;</span>

<span class="tok-c1"># Frontend</span>
<span class="tok-kn">location</span> <span class="tok-s">/</span> <span class="tok-p">{</span>
    <span class="tok-kn">root</span> <span class="tok-s">/home/taiga/taiga-front-dist/dist/</span><span class="tok-p">;</span>
    <span class="tok-kn">try_files</span> <span class="tok-nv">$uri</span> <span class="tok-nv">$uri/</span> <span class="tok-s">/index.html</span><span class="tok-p">;</span>
<span class="tok-p">}</span>

<span class="tok-c1"># Backend</span>
<span class="tok-kn">location</span> <span class="tok-s">/api</span> <span class="tok-p">{</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">Host</span> <span class="tok-nv">$http_host</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Real-IP</span> <span class="tok-nv">$remote_addr</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Scheme</span> <span class="tok-nv">$scheme</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Forwarded-Proto</span> <span class="tok-nv">$scheme</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Forwarded-For</span> <span class="tok-nv">$proxy_add_x_forwarded_for</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_pass</span> <span class="tok-s">http://127.0.0.1:8001/api</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_redirect</span> <span class="tok-no">off</span><span class="tok-p">;</span>
<span class="tok-p">}</span>

<span class="tok-c1"># Django admin access (/admin/)</span>
<span class="tok-kn">location</span> <span class="tok-s">/admin</span> <span class="tok-p">{</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">Host</span> <span class="tok-nv">$http_host</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Real-IP</span> <span class="tok-nv">$remote_addr</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Scheme</span> <span class="tok-nv">$scheme</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Forwarded-Proto</span> <span class="tok-nv">$scheme</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Forwarded-For</span> <span class="tok-nv">$proxy_add_x_forwarded_for</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_pass</span> <span class="tok-s">http://127.0.0.1:8001</span><span class="tok-nv">$request_uri</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_redirect</span> <span class="tok-no">off</span><span class="tok-p">;</span>
<span class="tok-p">}</span>

<span class="tok-c1"># Static files</span>
<span class="tok-kn">location</span> <span class="tok-s">/static</span> <span class="tok-p">{</span>
    <span class="tok-kn">alias</span> <span class="tok-s">/home/taiga/taiga-back/static</span><span class="tok-p">;</span>
<span class="tok-p">}</span>

<span class="tok-c1"># Media files</span>
<span class="tok-kn">location</span> <span class="tok-s">/media</span> <span class="tok-p">{</span>
    <span class="tok-kn">alias</span> <span class="tok-s">/home/taiga/taiga-back/media</span><span class="tok-p">;</span>
<span class="tok-p">}</span>

}

Disable the default nginx site (virtualhost)
sudo rm /etc/nginx/sites-enabled/default
Enable the recently created Taiga site (virtualhost)
sudo ln -s /etc/nginx/sites-available/taiga /etc/nginx/sites-enabled/taiga

You can verify the nginx configuration with the following command to track any error preventing the service to start with sudo nginx -t.

Finally, restart nginx with sudo systemctl restart nginx.

Now you should have the service up and running on http://example.com/

6.2.1. Serving HTTPS

Place your SSL certificates in /etc/nginx/ssl. It is recommended to replace the original configuration for port 80 so that users are redirected to the HTTPS version automatically.

Second we need to generate a stronger GHE parameter

cd /etc/ssl
sudo openssl dhparam -out dhparam.pem 4096
New configuration in /etc/nginx/sites-available/taiga
server {
    listen 80 default_server;
    server_name _;
    return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl default_server;
server_name _;

<span class="tok-kn">large_client_header_buffers</span> <span class="tok-mi">4</span> <span class="tok-mi">32k</span><span class="tok-p">;</span>
<span class="tok-kn">client_max_body_size</span> <span class="tok-s">50M</span><span class="tok-p">;</span>
<span class="tok-kn">charset</span> <span class="tok-s">utf-8</span><span class="tok-p">;</span>

<span class="tok-kn">index</span> <span class="tok-s">index.html</span><span class="tok-p">;</span>

<span class="tok-c1"># Frontend</span>
<span class="tok-kn">location</span> <span class="tok-s">/</span> <span class="tok-p">{</span>
    <span class="tok-kn">root</span> <span class="tok-s">/home/taiga/taiga-front-dist/dist/</span><span class="tok-p">;</span>
    <span class="tok-kn">try_files</span> <span class="tok-nv">$uri</span> <span class="tok-nv">$uri/</span> <span class="tok-s">/index.html</span><span class="tok-p">;</span>
<span class="tok-p">}</span>

<span class="tok-c1"># Backend</span>
<span class="tok-kn">location</span> <span class="tok-s">/api</span> <span class="tok-p">{</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">Host</span> <span class="tok-nv">$http_host</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Real-IP</span> <span class="tok-nv">$remote_addr</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Scheme</span> <span class="tok-nv">$scheme</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Forwarded-Proto</span> <span class="tok-nv">$scheme</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Forwarded-For</span> <span class="tok-nv">$proxy_add_x_forwarded_for</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_pass</span> <span class="tok-s">http://127.0.0.1:8001/api</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_redirect</span> <span class="tok-no">off</span><span class="tok-p">;</span>
<span class="tok-p">}</span>

<span class="tok-kn">location</span> <span class="tok-s">/admin</span> <span class="tok-p">{</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">Host</span> <span class="tok-nv">$http_host</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Real-IP</span> <span class="tok-nv">$remote_addr</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Scheme</span> <span class="tok-nv">$scheme</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Forwarded-Proto</span> <span class="tok-nv">$scheme</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_set_header</span> <span class="tok-s">X-Forwarded-For</span> <span class="tok-nv">$proxy_add_x_forwarded_for</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_pass</span> <span class="tok-s">http://127.0.0.1:8001</span><span class="tok-nv">$request_uri</span><span class="tok-p">;</span>
    <span class="tok-kn">proxy_redirect</span> <span class="tok-no">off</span><span class="tok-p">;</span>
<span class="tok-p">}</span>

<span class="tok-c1"># Static files</span>
<span class="tok-kn">location</span> <span class="tok-s">/static</span> <span class="tok-p">{</span>
    <span class="tok-kn">alias</span> <span class="tok-s">/home/taiga/taiga-back/static</span><span class="tok-p">;</span>
<span class="tok-p">}</span>

<span class="tok-c1"># Media files</span>
<span class="tok-kn">location</span> <span class="tok-s">/media</span> <span class="tok-p">{</span>
    <span class="tok-kn">alias</span> <span class="tok-s">/home/taiga/taiga-back/media</span><span class="tok-p">;</span>
<span class="tok-p">}</span>

<span class="tok-kn">add_header</span> <span class="tok-s">Strict-Transport-Security</span> <span class="tok-s">"max-age=63072000</span><span class="tok-p">;</span> <span class="tok-kn">includeSubdomains</span><span class="tok-p">;</span> <span class="tok-kn">preload"</span><span class="tok-p">;</span>
<span class="tok-kn">add_header</span> <span class="tok-s">Public-Key-Pins</span> <span class="tok-s">'pin-sha256="klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY="</span><span class="tok-p">;</span> <span class="tok-kn">pin-sha256="633lt352PKRXbOwf4xSEa1M517scpD3l5f79xMD9r9Q="</span><span class="tok-p">;</span> <span class="tok-kn">max-age=2592000</span><span class="tok-p">;</span> <span class="tok-kn">includeSubDomains'</span><span class="tok-p">;</span>

<span class="tok-kn">ssl</span> <span class="tok-no">on</span><span class="tok-p">;</span>
<span class="tok-kn">ssl_certificate</span> <span class="tok-s">/etc/nginx/ssl/example.com/ssl-bundle.crt</span><span class="tok-p">;</span>
<span class="tok-kn">ssl_certificate_key</span> <span class="tok-s">/etc/nginx/ssl/example.com/example_com.key</span><span class="tok-p">;</span>
<span class="tok-kn">ssl_session_timeout</span> <span class="tok-mi">5m</span><span class="tok-p">;</span>
<span class="tok-kn">ssl_protocols</span> <span class="tok-s">TLSv1</span> <span class="tok-s">TLSv1.1</span> <span class="tok-s">TLSv1.2</span><span class="tok-p">;</span>
<span class="tok-kn">ssl_prefer_server_ciphers</span> <span class="tok-no">on</span><span class="tok-p">;</span>
<span class="tok-kn">ssl_ciphers</span> <span class="tok-s">'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK'</span><span class="tok-p">;</span>
<span class="tok-kn">ssl_session_cache</span> <span class="tok-s">shared:SSL:10m</span><span class="tok-p">;</span>
<span class="tok-kn">ssl_dhparam</span> <span class="tok-s">/etc/ssl/dhparam.pem</span><span class="tok-p">;</span>
<span class="tok-kn">ssl_stapling</span> <span class="tok-no">on</span><span class="tok-p">;</span>
<span class="tok-kn">ssl_stapling_verify</span> <span class="tok-no">on</span><span class="tok-p">;</span>

}

Before activating the HTTPS site, the configuration for the front-end and back-end have to be updated; change the scheme from http to https.

Update ~/taiga-back/settings/local.py
from .common import *

MEDIA_URL = "https://example.com/media/"
STATIC_URL = "https://example.com/static/"
ADMIN_MEDIA_PREFIX = "https://example.com/static/admin/"
SITES["front"]["scheme"] = "https"
SITES["front"]["domain"] = "example.com"

SECRET_KEY = "theveryultratopsecretkey"

DEBUG = False
TEMPLATE_DEBUG = False
PUBLIC_REGISTER_ENABLED = True

DEFAULT_FROM_EMAIL = "no-reply@example.com"
SERVER_EMAIL = DEFAULT_FROM_EMAIL

# Uncomment and populate with proper connection parameters
# for enable email sending.
#EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
#EMAIL_USE_TLS = False
#EMAIL_HOST = "localhost"
#EMAIL_HOST_USER = ""
#EMAIL_HOST_PASSWORD = ""
#EMAIL_PORT = 25

# Uncomment and populate with proper connection parameters
# for enable github login/singin.
#GITHUB_API_CLIENT_ID = "yourgithubclientid"
#GITHUB_API_CLIENT_SECRET = "yourgithubclientsecret"

Restart circus after updating the configuration
sudo systemctl restart circus 
Update ~/taiga-front-dist/dist/conf.json
{
    "api": "https://example.com/api/v1/",
    "eventsUrl": "wss://example.com/events",
    "debug": "true",
    "publicRegisterEnabled": true,
    "feedbackEnabled": true,
    "privacyPolicyUrl": null,
    "termsOfServiceUrl": null,
    "maxUploadFileSize": null
}

And nginx:

Reload the nginx configuration
systemctl reload nginx