Developer Setup

The following describes how to set up an instance of the site on your computer for development.


This guide assumes you have already installed and set up the following:

  1. Git

  2. Python 3.9, and Poetry

  3. Node.js 8 and NPM 5 or higher

  4. Postgres 9.4

  5. openssl

These docs assume a Unix-like operating system, although the site should, in theory, run on Windows as well. All the example commands given below are intended to be run in a terminal.


  1. Clone this repository or your fork:

    git clone
    cd normandy
  2. Install the dependencies using Poetry:

    poetry install
  3. Activate the poetry virtual environment. The easiest way to do this is to start a new shell with Poetry.

    poetry shell

  4. (Optional) Install frontend dependencies using yarn and build the legacy action code:

    yarn install
    yarn build

    This JS code is only used for legacy clients that do not have actions implemented directly in the client. It can be safely ignored in most development environments.

  1. Create a Postgres database for Normandy. By default it is assumed to be named normandy:

    createdb normandy


    By default, it will connect to localhost using the global postgres user. If you use a different name for the database, or otherwise want to customize how you connect to the database, you may specify the database URL by adding the following to a .env file at the root of the repo:

  2. Initialize your database by running the migrations:

    python migrate
  3. Create a new superuser account:

    python createsuperuser
  4. (Optional) Pull the latest geolocation database using the script:


    The geolocation database is used for the classify client API endpoint. It is required for recipes that use geolocation filters, but can be omitted.

  5. (Optional) If you built the JS actions, load them into the database:

    python update_actions
  6. Update product details to provide a list of languages supported by Firefox.

python update_product_details
  1. Load in initial data:

python initial_data

Once you’ve finished these steps, you should be able to start the site by running:



The command automatically creates a self-signed certificate in the etc/ssl directory of the repository. When viewing the site for the first time, you will have to create a certificate exception to allow Firefox to accept the certificate and access the site over HTTPS.

The site should be available at https://localhost:8000/. The main page will have the message “This interface has been removed”. The API is available at the endpoint /api/v3/. No user interface is provided.

Alternative Setup and Use

If you would like to use docker instead please reference the docker section.


If you want to execute recipes on your local instance using the recipe client, you’ll need to set up Autograph to sign recipes as you save them:

  1. Follow the Autograph installation instructions to launch a development instance of Autograph.

  2. Add the following configuration to .env (create the file if it does not exist yet):


With the configuration in place, you should see log messages when saving recipes that look like this:

INFO 2017-05-01 19:58:04,274 Requesting signatures for recipes with ids [16] from Autograph
INFO 2017-05-01 19:58:04,301 Got 1 signatures from Autograph


If you want to automatically enforce Normandy code style guidelines, you can use the Therapist pre-commit hook. To install Therapist, simply run:

pip install therapist

After that, you should be able to run the following to set up the git pre-commit hook:

therapist install

After that, whenever you make a new commit Therapist will check the changed code. This will save time when submitting pull requests.

If you want Therapist to attempt to automatically fix linting issues you can install the hook using:

therapist install --fix

If you ever need to bypass Therapist, you can do so by passing --no-verify to your git commit command.

Remote Settings

If you want to enable the publication of recipes on Remote Settings, you’ll need to set up an instance locally.

  1. Follow the Remote Settings installation instructions to launch a development instance of Remote Settings locally.

  2. Configure Normandy to enable the integration by adding the following lines to .env (create the file if it does not exist yet):

  3. Adjust the Remote Settings configuration in server.ini for the Normandy recipes collection, and restart the Remote Settings container:

    kinto.signer.main-workspace.normandy-recipes.to_review_enabled = false
    kinto.signer.main-workspace.normandy-recipes.group_check_enabled = false
  4. Create the dedicated Remote Settings user and collection for Normandy:

    curl -X PUT ${SERVER}/accounts/normandy \
         -d '{"data": {"password": "n0rm4ndy"}}' \
         -H 'Content-Type:application/json'
    curl -X PUT ${SERVER}/buckets/main-workspace \
         -H 'Content-Type:application/json' \
         -u 'normandy:n0rm4ndy'
    curl -X PUT ${SERVER}/buckets/main-workspace/collections/normandy-recipes \
         -H 'Content-Type:application/json' \
         -u 'normandy:n0rm4ndy'
    curl -X PUT ${SERVER}/buckets/main-workspace/collections/normandy-recipes-capabilities \
         -H 'Content-Type:application/json' \
         -u 'normandy:n0rm4ndy'

With both configurations in place, Normandy should start without error.