diff --git a/group_vars/printer.yml b/group_vars/printer.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f0e26d9887ca137e39247599af3250808ee99a51
--- /dev/null
+++ b/group_vars/printer.yml
@@ -0,0 +1,65 @@
+---
+glob_printer:
+  django_secret_key: "{{ vault.printer.django_secret_key }}"
+  admins:
+    - ('Root', 'root@crans.org')
+  allowed_hosts:
+    - 'helloworld.crans.org'
+    - 'imprimante.crans.org'
+  email:
+    ssl: false
+    host: "{{ query('ldap', 'ip', 'redisdead', 'adm') | ipv4 | first }}"
+    port: 25
+    user: ''
+    password: ''
+    from: "root@crans.org"
+    from_full: "Crans <root@crans.org>"
+  database:
+    host: "{{ query('ldap', 'ip', 'tealc', 'adm') | ipv4 | first }}"
+    port: 5432
+    user: 'helloworld'
+    password: "{{ vault.printer.django_db_password }}"
+    name: 'helloworld'
+  note:
+    url: 'https://note.crans.org/'
+    client_id: '{{ vault.printer.note.client_id }}'
+    client_secret: '{{ vault.printer.note.client_secret }}'
+    note_id: 2088
+    note_alias: 'Crans'
+  printer_name: 'Lexmark_X950_Series'
+  debug: false
+  owner: root
+  group: _nounou
+  version: main
+  settings_local_owner: www-data
+  settings_local_group: _nounou
+
+loc_nginx:
+  service_name: printer
+  ssl: []
+  servers:
+    - ssl: false
+      default: true
+      server_name:
+        - "helloworld.crans.org"
+        - "imprimante.crans.org"
+      locations:
+        - filter: "/static"
+          params:
+            - "alias {% if printer.version == 'main' %}/var/lib/django-printer/static/{% else %}/var/local/django-printer/static/{% endif %}"
+
+        - filter: "/media"
+          params:
+            - "alias {% if printer.version == 'main' %}/var/lib/django-printer/media/{% else %}/var/local/django-printer/media/{% endif %}"
+
+        - filter: "/doc"
+          params:
+            - "alias /var/www/django-printer-doc/"
+
+        - filter: "/"
+          params:
+            - "uwsgi_pass printer"
+            - "include /etc/nginx/uwsgi_params"
+  upstreams:
+    - name: 'printer'
+      server: 'unix:///var/run/uwsgi/app/django-printer/printer.sock'
diff --git a/host_vars/helloworld.adm.crans.org.yml b/host_vars/helloworld.adm.crans.org.yml
index 92076e1af085513ba213226f7ceebaafd6bd4752..b50a0ea79761b21ef77bbf4fd8aec477123b1254 100644
--- a/host_vars/helloworld.adm.crans.org.yml
+++ b/host_vars/helloworld.adm.crans.org.yml
@@ -2,3 +2,13 @@
 interfaces:
   adm: ens18
   srv_nat: ens19
+
+loc_printer:
+  note:
+    url: 'https://note-dev.crans.org/'
+    client_id: '{{ vault.printer.note.client_id }}'
+    client_secret: '{{ vault.printer.note.client_secret }}'
+    note_id: 2088
+    note_alias: 'Crans'
+  debug: true
+  version: main
diff --git a/roles/constellation/tasks/main.yml b/roles/constellation/tasks/main.yml
index e09352d22d574641c68bc6a524fd32a5877eba61..af533e782fa156e00d26ad71d5ecc7b054fe0efc 100644
--- a/roles/constellation/tasks/main.yml
+++ b/roles/constellation/tasks/main.yml
@@ -15,6 +15,7 @@
       - python3-django-polymorphic
       - python3-ipython
       - python3-pip
+      - python3-psycopg2
       - python3-requests
   register: apt_result
   retries: 3
diff --git a/roles/printer/handlers/main.yml b/roles/printer/handlers/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..73c9606aae8641cd68842712edb70f72054a5ae6
--- /dev/null
+++ b/roles/printer/handlers/main.yml
@@ -0,0 +1,5 @@
+---
+- name: Restart uWSGI
+  systemd:
+    name: uwsgi
+    state: restarted
diff --git a/roles/printer/tasks/main.yml b/roles/printer/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0c63b30f120f08bef9d82bec9cd75e6cf29e80bc
--- /dev/null
+++ b/roles/printer/tasks/main.yml
@@ -0,0 +1,184 @@
+---
+- name: Pin Django from Debian bullseye-backports
+  template:
+    src: "apt/sources.list.d/bullseye-backports.list.j2"
+    dest: "/etc/apt/sources.list.d/bullseye-backports.list"
+
+- name: Install printer dependencies
+  apt:
+    update_cache: true
+    install_recommends: false
+    name:
+      - cups
+      - gettext
+      - python3-authlib
+      - python3-django
+      - python3-django-extensions
+      - python3-docutils
+      - python3-ipython
+      - python3-pip
+      - python3-cups
+      - python3-psycopg2
+      - python3-pypdf2
+      - python3-requests
+      - python3-sphinx
+      - python3-sphinx-rtd-theme
+      - uwsgi
+      - uwsgi-plugin-python3
+  register: apt_result
+  retries: 3
+  until: apt_result is succeeded
+
+- name: Set configuration directories in development mode
+  when: printer.version != "main"
+  set_fact:
+    module_path: "/var/local/django-printer/printer"
+    project_path: "/var/local/django-printer"
+
+- name: Set configuration directories in production mode
+  when: printer.version == "main"
+  set_fact:
+    module_path: "/usr/local/lib/python3.9/dist-packages/printer"
+    project_path: "/usr/local/lib/python3.9/dist-packages/printer"
+
+- name: Create django-printer configuration directory
+  file:
+    path: "/etc/django-printer"
+    state: directory
+    mode: '2775'
+    owner: "{{ printer.owner }}"
+    group: "{{ printer.group }}"
+
+- name: Set ACL for printer directory
+  acl:
+    path: "/etc/django-printer"
+    default: true
+    entity: _nounou
+    etype: group
+    permissions: rwx
+    state: query
+  ignore_errors: "{{ ansible_check_mode }}"
+
+- name: Clone printer repository (development)
+  when: printer.version != "main"
+  git:
+    repo: 'https://gitlab.adm.crans.org/nounous/django-printer.git'
+    dest: "{{ project_path }}"
+    umask: '002'
+    version: "{{ printer.version }}"
+    recursive: true
+
+- name: Install pip module with editable flag (development)
+  when: printer.version != "main"
+  pip:
+    name:
+      - "{{ project_path }}"
+    editable: true
+    state: latest
+
+- name: Install and upgrade django-printer (production)
+  when: printer.version == "main"
+  pip:
+    name:
+      - git+https://gitlab.adm.crans.org/nounous/django-printer.git
+    state: latest
+
+- name: Set owner of cloned project
+  when: printer.version != "main"
+  file:
+    path: "{{ project_path }}"
+    owner: "{{ printer.owner }}"
+    group: "{{ printer.group }}"
+    recurse: true
+
+- name: Set manage.py executable
+  file:
+    path: "{{ module_path }}/manage.py"
+    mode: 0755
+
+- name: Deploy local settings
+  template:
+    src: django-printer/settings_local.py.j2
+    dest: "/etc/django-printer/settings_local.py"
+    mode: 0660
+
+- name: Symlink configuration file
+  file:
+    src: "/etc/django-printer/settings_local.py"
+    dest: "{{ module_path }}/settings_local.py"
+    state: link
+  ignore_errors: "{{ ansible_check_mode }}"
+
+# In the future, migrations will be included in the repository.
+- name: Make Django migrations
+  django_manage:
+    command: makemigrations
+    project_path: "{{ project_path }}"
+  notify: Restart uWSGI
+
+- name: Migrate database
+  django_manage:
+    command: migrate
+    project_path: "{{ project_path }}"
+  notify: Restart uWSGI
+
+- name: Create static files directory
+  file:
+    path: "/var/lib/django-printer/{{ item }}"
+    state: directory
+    mode: '2775'
+    owner: "www-data"
+    group: "{{ printer.group }}"
+    recurse: true
+  loop:
+    - static
+    - files
+
+- name: Collect static files
+  django_manage:
+    command: collectstatic
+    project_path: "{{ project_path }}"
+  notify: Restart uWSGI
+
+- name: Compile messages
+  django_manage:
+    command: compilemessages
+    project_path: "{{ project_path }}"
+
+- name: Copy uWSGI app
+  template:
+    src: "uwsgi/apps-available/printer.ini.j2"
+    dest: "/etc/uwsgi/apps-available/printer.ini"
+    owner: root
+    group: root
+    mode: 0644
+  notify: Restart uWSGI
+
+- name: Activate uWSGI app
+  file:
+    src: "../apps-available/printer.ini"
+    dest: "/etc/uwsgi/apps-enabled/printer.ini"
+    owner: root
+    group: root
+    state: link
+  ignore_errors: "{{ ansible_check_mode }}"
+  notify: Restart uWSGI
+
+
+- name: Create documentation directory with good permissions
+  file:
+    path: /var/www/django-printer-doc
+    state: directory
+    owner: www-data
+    group: www-data
+    mode: u=rwx,g=rwxs,o=rx
+
+- name: Build HTML documentation
+  command: "sphinx-build -b dirhtml {{ project_path }}/docs/ /var/www/django-printer-doc/"
+  become_user: www-data
+
+- name: Indicate module in motd
+  template:
+    src: update-motd.d/05-service.j2
+    dest: /etc/update-motd.d/05-django-printer
+    mode: 0755
diff --git a/roles/printer/templates/apt/sources.list.d/bullseye-backports.list.j2 b/roles/printer/templates/apt/sources.list.d/bullseye-backports.list.j2
new file mode 100644
index 0000000000000000000000000000000000000000..e231539d06d56f121ffe79c292d821e2b73543a7
--- /dev/null
+++ b/roles/printer/templates/apt/sources.list.d/bullseye-backports.list.j2
@@ -0,0 +1,3 @@
+{{ ansible_header | comment }}
+
+deb {{ debian_mirror }} bullseye-backports main
diff --git a/roles/printer/templates/django-printer/settings_local.py.j2 b/roles/printer/templates/django-printer/settings_local.py.j2
new file mode 100644
index 0000000000000000000000000000000000000000..f206cf28867dfd51a77c13bbcd93efdb35378652
--- /dev/null
+++ b/roles/printer/templates/django-printer/settings_local.py.j2
@@ -0,0 +1,46 @@
+{{ ansible_header | comment }}
+
+# A secret key used by the server.
+SECRET_KEY = "{{ printer.django_secret_key }}"
+
+# Should the server run in debug mode ?
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = {{ printer.debug }}
+
+# A list of admins of the services. Receive mails when an error occurs
+ADMINS = [{% for admin in printer.admins %}{{ admin }}, {% endfor %}]
+
+# The list of hostname the server will respond to.
+ALLOWED_HOSTS = [{% for host in printer.allowed_hosts %}'{{ host }}', {% endfor %}]
+
+# The storage systems parameters to use
+DATABASES = {
+    'default': {  # The DB
+        'ENGINE': 'django.db.backends.postgresql',
+        'NAME': '{{ printer.database.name }}',
+        'USER': '{{ printer.database.user }}',
+        'PASSWORD': "{{ printer.database.password }}",
+        'HOST': '{{ printer.database.host }}',
+        'PORT': '{{ printer.database.port }}',
+    },
+}
+
+{% if printer.version == "main" %}
+STATIC_ROOT = "/var/lib/django-printer/static/"
+
+{% endif %}
+MEDIA_ROOT = "/var/lib/django-printer/files/"
+
+# The mail configuration to send mails
+EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
+EMAIL_USE_SSL = {{ printer.email.ssl }}
+EMAIL_HOST = '{{ printer.email.host }}'
+EMAIL_PORT = {{ printer.email.port }}
+EMAIL_HOST_USER = '{{ printer.email.user }}'
+EMAIL_HOST_PASSWORD = '{{ printer.email.password }}'
+SERVER_EMAIL = '{{ printer.email.from }}'
+DEFAULT_FROM_EMAIL = '{{ printer.email.from_full }}'
+
+NOTE_KFET_URL = "{{ printer.note.url }}"
+NOTE_KFET_CLIENT_ID = "{{ printer.note.client_id }}"
+NOTE_KFET_CLIENT_SECRET = "{{ printer.note.client_secret }}"
diff --git a/roles/printer/templates/update-motd.d/05-service.j2 b/roles/printer/templates/update-motd.d/05-service.j2
new file mode 100755
index 0000000000000000000000000000000000000000..81b91a7e248f453f0f96a110da6ddc5c4c464883
--- /dev/null
+++ b/roles/printer/templates/update-motd.d/05-service.j2
@@ -0,0 +1,3 @@
+#!/usr/bin/tail +14
+{{ ansible_header | comment }}
+> Django-printer a été déployé sur cette machine. Voir {{ project_path }}/.
diff --git a/roles/printer/templates/uwsgi/apps-available/printer.ini.j2 b/roles/printer/templates/uwsgi/apps-available/printer.ini.j2
new file mode 100644
index 0000000000000000000000000000000000000000..d2a0e98d651f14405276cd4559b4daceaa82c22d
--- /dev/null
+++ b/roles/printer/templates/uwsgi/apps-available/printer.ini.j2
@@ -0,0 +1,23 @@
+{{ ansible_header | comment }}
+
+[uwsgi]
+uid             = www-data
+gid             = www-data
+# Django-related settings
+# the base directory (full path)
+chdir           = {{ project_path }}
+wsgi-file       = {{ module_path }}/wsgi.py
+plugin          = python3
+# process-related settings
+# master
+master          = true
+# maximum number of worker processes
+processes       = 10
+# the socket (use the full path to be safe)
+socket          = /var/run/uwsgi/app/django-printer/django-printer.sock
+# ... with appropriate permissions - may be needed
+chmod-socket    = 664
+# clear environment on exit
+vacuum          = true
+# Touch reload
+touch-reload = {{ module_path }}/settings.py