Kiwi TCMS configuration settings

All sensible settings are defined in tcms/settings/ You will have to update some of them for your particular production environment.


After adjusting settings for your environment you have to configure Kiwi TCMS via its web interface! As a minimum you have to Configuration of Kiwi TCMS domain and Configure external bug trackers!


Additional information how to override Kiwi TCMS behavior can be found at!

# -*- coding: utf-8 -*-

import os
import pathlib
import tempfile
from importlib import import_module

import pkg_resources
from django.contrib.messages import constants as messages
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _

import tcms

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~ You have to override the following settings in

# Set to False for production
DEBUG = True

# Make this unique, and don't share it with anybody.
SECRET_KEY = "^8y!)$0t7yq2+65%&_#@i^_o)eb3^q--y_$e7a_=t$%$1i)zuv"  # nosec:B105

# Database settings
    "default": {
        "ENGINE": os.environ.get("KIWI_DB_ENGINE", "django.db.backends.mysql"),
        "NAME": os.environ.get("KIWI_DB_NAME", "kiwi"),
        "USER": os.environ.get("KIWI_DB_USER", "kiwi"),
        "PASSWORD": os.environ.get("KIWI_DB_PASSWORD", "kiwi"),
        "HOST": os.environ.get("KIWI_DB_HOST", ""),
        "PORT": os.environ.get("KIWI_DB_PORT", ""),
        "OPTIONS": {},

# handle MariaDB only options
if DATABASES["default"]["ENGINE"].find("mysql") > -1:
    DATABASES["default"]["OPTIONS"].update(  # pylint: disable=objects-update-used
            "init_command": "SET sql_mode='STRICT_TRANS_TABLES'",
            "charset": "utf8mb4",

# Administrators error report email settings
    # ('Your Name', ''),

# Email settings
# DEFAULT_FROM_EMAIL must be defined if you want Kiwi TCMS to send emails.
# You also need to configure the email backend. For more information see:
# SERVER_EMAIL is used by the logging backend to send exceptions to ADMINS

#  SMTP specific settings
#  EMAIL_HOST = ''
#  EMAIL_PORT = 25
#  EMAIL_HOST_USER = 'smtp_username'
#  EMAIL_HOST_PASSWORD = 'smtp_password'

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~ You may want to override the following settings as well

# Hosts/domain names that are valid for this site; required if DEBUG is False
# See

# default group in which new users will be created

# When set to False site administrators will have to manually approve
# new users. You can combine this with tcms.signals.notify_admins() signal
# handler!

# How often will session cookies expire? We set this to 24hrs by default.
# You may override based on your security policies

# Maximum upload file size, default set to 5MB.

# Controls if django-attachments deletes files from disk

# Configure a caching backend. ATM only used to cache bug details b/c
# external issue trackers may be slow. If you want to override see:
    "default": {
        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
        "LOCATION": "kiwitcms",
        "TIMEOUT": 3600,

# Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/var/www/"
STATIC_ROOT = "/Kiwi/static/"


# WARNING: Do not change this unless you know what you are doing !!!

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~ DANGER: Don't change the settings below!


KIWI_VERSION = tcms.__version__


LOGIN_REDIRECT_URL = reverse_lazy("core-views-index")

# internal
TCMS_ROOT_PATH = os.path.abspath(
    os.path.join(os.path.dirname(__file__), "..").replace("\\", "/")

# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True

# If you set this to False, Django will not format dates, numbers and
# calendars according to the current locale.
USE_L10N = True

# Language code for this installation. All choices can be found here:
# See

    os.path.join(TCMS_ROOT_PATH, "locale"),

# If you set this to False, Django will not use timezone-aware datetimes.
# See
USE_TZ = os.environ.get("KIWI_USE_TZ", "False").lower() == "true"

# Local time zone for this installation. Choices can be found here:
TIME_ZONE = os.environ.get("KIWI_TIME_ZONE", "Etc/UTC")

# Absolute filesystem path to the directory that will hold user-uploaded files.
MEDIA_ROOT = "/Kiwi/uploads"

# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash.
# Examples: "", ""
MEDIA_URL = "/uploads/"

# URL prefix for static files.
# Example: "", ""
STATIC_URL = "/static/"

# Additional locations of static files
    # Put strings here, like "/home/html/static" or "C:/www/django/static".
    # Always use forward slashes, even on Windows.
    # Don't forget to use absolute paths, not relative paths.
    os.path.join(TCMS_ROOT_PATH, "static").replace("\\", "/"),
    os.path.join(TCMS_ROOT_PATH, "node_modules").replace("\\", "/"),

# List of finder classes that know how to find static files in
# various locations.

        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [
            os.path.join(TCMS_ROOT_PATH, "templates/").replace("\\", "/"),
        "OPTIONS": {
            "context_processors": [
            "loaders": [

ROOT_URLCONF = "tcms.urls"

# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = "tcms.wsgi.application"

# DANGER: DO NOT EDIT, see git log !!!
# this is consumed by kiwitcms-tenants/django-tenants

# if you wish to disable Kiwi TCMS bug tracker
# define the KIWI_DISABLE_BUGTRACKER ENV variable
if os.environ.get("KIWI_DISABLE_BUGTRACKER") is None:


for plugin in pkg_resources.iter_entry_points("kiwitcms.plugins"):

# this is the main navigation menu
    (_("DASHBOARD"), reverse_lazy("core-views-index")),
            (_("New Test Plan"), reverse_lazy("plans-new")),
            ("-", "-"),
            (_("New Test Case"), reverse_lazy("testcases-new")),
            ("-", "-") if "tcms.bugs.apps.AppConfig" in INSTALLED_APPS else (),
            (_("New Bug"), reverse_lazy("bugs-new"))
            if "tcms.bugs.apps.AppConfig" in INSTALLED_APPS
            else (),
            (_("Search Test Plans"), reverse_lazy("plans-search")),
            (_("Search Test Runs"), reverse_lazy("testruns-search")),
            (_("Search Test Cases"), reverse_lazy("testcases-search")),
            (_("Search Bugs"), reverse_lazy("bugs-search"))
            if "tcms.bugs.apps.AppConfig" in INSTALLED_APPS
            else (),
                    (_("Breakdown"), reverse_lazy("testing-breakdown")),
                    (_("Status matrix"), reverse_lazy("testing-status-matrix")),
                    (_("Execution trends"), reverse_lazy("testing-execution-trends")),
                    (_("TestCase health"), reverse_lazy("test-case-health")),
                "More coming soon",
            (_("Users and groups"), "/admin/auth/"),
            ("-", "-"),
            (_("Everything else"), "/admin/"),
    (_("PLUGINS"), []),

# last element is always PLUGINS so we can easily extend & override it
for plugin in pkg_resources.iter_entry_points("kiwitcms.plugins"):
    plugin_menu = import_module("" % plugin.module_name)

# redefine the help menu in the navigation bar
    ("", _("Forum")),
    ("", _("Report an Issue")),
        _("Ask for help on StackOverflow"),
        _("Donate €5 via Open Collective"),
    ("", _("Administration Guide")),
    ("", _("User Guide")),
    ("", _("API Help")),

SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"

SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"

# WARNING: do not edit. The stock JSONRPC handler does not HTML escape !!!

# in alphabetic order

if "tcms.bugs.apps.AppConfig" in INSTALLED_APPS:

# This is a list of dotted class names providing integration with
# external bug trackers. Plugins and downstream installation can augment
# this list with their own integration classes!
if "tcms.bugs.apps.AppConfig" in INSTALLED_APPS:

# Enable the administrator delete permission
# In another word it's set the admin to super user or not.

# Allows history_change_reason to be a TextField so we can
# support a changelog-like feature! DO NOT EDIT b/c migrations
# depend on this setting!

# Default page size when paginating queries

# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error when DEBUG=False.
# See for
# more details on how to customize your logging configuration.
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "verbose": {
            "format": "%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s"
        "simple": {"format": "[%(asctime)s] %(levelname)s %(message)s"},
    "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}},
    "handlers": {
        "console": {
            "level": "DEBUG",
            "class": "logging.StreamHandler",
            "formatter": "simple",
        "mail_admins": {
            "level": "ERROR",
            "filters": ["require_debug_false"],
            "class": "django.utils.log.AdminEmailHandler",
    "loggers": {
        "django.request": {
            "handlers": ["mail_admins"],
            "level": "ERROR",
            "propagate": True,

# override default message tags to match Patternfly class names
    messages.ERROR: "danger",

TEMP_DIR = pathlib.Path(tempfile.gettempdir())

# See

Augmenting behavior with signal handlers

Kiwi TCMS defines signals which are triggered during specific events. Administrators may override the default handlers and/or connect additional ones to augment the default behavior of Kiwi TCMS. For more information refer to tcms.signals.

Language and emojis

By default our docker-compose.yml file is configured with MariaDB and uses charset utf8mb4 and collation utf8mb4_unicode_ci. That should be sufficient to support many languages plus emojis. If you are having troubles consult MariaDB Character Sets and Collations documentation.

If you need to change the default settings see docker-compose.yml or /etc/mysql/conf.d/mariadb.cnf if using a stand alone DB server! On the application side see DATABASES['default']['OPTIONS'] in tcms/settings/

Time zone settings

By default Kiwi TCMS is configured to use the UTC time zone. /etc/localtime inside the docker image also points to /usr/share/zoneinfo/Etc/UTC!

The settings which control this behavior are:

  • USE_TZ - controls whether or not Django uses timezone-aware datetime objects internally. Can also be controlled via KIWI_USE_TZ environment variable
  • TIME_ZONE - a string representing the time zone for this application. This should match the actual configuration of your server/docker container! Can also be controlled via KIWI_TIME_ZONE environment variable.

Your server/docker container clock should also match the accurate time of day!


Unlike language there is no way to automatically receive time zone information from the browser and display different values for users. Instead Kiwi TCMS displays the current server time & time zone in the navigation bar! All other timestamps are in the same time zone!

E-mail settings

Kiwi TCMS supports email notifications which by default are sent over SMTP and need to be configured via the following settings:

  • Common settings:

    # additional Kiwi TCMS setting
  • SMTP specific settings:

    EMAIL_HOST = ''
    EMAIL_PORT = 25
    EMAIL_HOST_USER = 'smtp_username'
    EMAIL_HOST_PASSWORD = 'smtp_password'

To enable SSL or TLS support include one of the following:


For more details refer to Django documentation at

Testing and debugging e-mail configuration

Sending test e-mail to one or more addresses:

docker exec -it kiwi_web /Kiwi/ sendtestemail user1@example1.tld user2@example2.tld ...

More details at:

Using Amazon SES instead of SMTP email

If you’d like to use an external email service, like Amazon SES you need to configure the following settings too:

EMAIL_BACKEND = 'django_ses.SESBackend'
AWS_SES_ACCESS_KEY_ID = 'xxxxxxxxxxxxxxxxxxxx'

Also modify Dockerfile to include the following lines:

RUN pip install django_ses

Use reCAPTCHA during registration

New in version 8.7.

If you want to use Google reCAPTCHA on the registration page then add the following to your settings:

if 'captcha' not in INSTALLED_APPS:



This is not enabled by default because the django-recaptcha library will cause Kiwi TCMS to stop working if the appropriate keys are not provided!

Kerberos authentication

See kiwitcms-auth-kerberos.

Anonymous read-only access

Changed in version 8.7.

By default Kiwi TCMS requires permissions, including view permissions which means users must be logged in! If you wish to allow anonymous read-only access then define the following setting:


New in version 8.7: tcms.kiwi_auth.backends.AnonymousViewBackend was added instead of the previous PUBLIC_VIEWS setting.

Changed in version 8.8: guardian.backends.ObjectPermissionBackend was added and the AUTHENTICATION_BACKENDS setting is now explicitly specified!