diff --git a/roles/netbox/README.md b/roles/netbox/README.md
new file mode 100644
index 0000000..38b7968
--- /dev/null
+++ b/roles/netbox/README.md
@@ -0,0 +1,77 @@
+# `netbox` role
+
+A role for setting up NetBox.  
+It automatically pulls in all required dependencies like Redis and PostgreSQL, deploys the provided systemd services and gunicorn config and sets up a PostgreSQL database named `netbox` with an owner named `netbox` and the specified password.
+However providing the [NetBox configuration](#netbox-configuration), [setting up a web server like nginx to proxy to gunicorn](#web-server-setup) and tasks like creating users, etc. you have to do yourself.
+
+## Supported Distributions
+
+Should work on Debian-based distributions.
+
+## Required Arguments
+
+- `netbox__version`: The NetBox version to deploy.
+- `netbox__db_password`: The password to use for connection to the database.
+  This is required since the upgrade script runs as root and therefore peer authentication doesn't work.
+- `netbox__config`: The NetBox config to deploy.
+  See [NetBox Configuration](#netbox-configuration) for more infos.
+
+## Optional Arguments
+
+None.
+
+## NetBox Configuration
+
+The NetBox configuration should include a connection to Redis as well as a connection to PostgreSQL.  
+Configuration for the Redis connection:
+
+```python
+REDIS = {
+    "tasks": {
+      "HOST": "localhost",
+      "PORT": 6379,
+      "USERNAME": "",
+      "PASSWORD": "",
+      "DATABASE": 0,
+      "SSL": False,
+    },
+    "caching": {
+      "HOST": "localhost",
+      "PORT": 6379,
+      "USERNAME": "",
+      "PASSWORD": "",
+      "DATABASE": 1,
+      "SSL": False,
+    },
+}
+```
+
+Configuration for the PostgreSQL connection:
+
+```python
+DATABASE = {
+  "HOST": "localhost",
+  "NAME": "netbox",
+  "USER": "netbox",
+  "PASSWORD": "<same as netbox__db_password>",
+}
+```
+
+Further configuration should take place. Some relevant resources can be found here:
+
+- Installation guide configuration docs: <https://netboxlabs.com/docs/netbox/en/stable/installation/3-netbox/#configuration>
+- Configuration docs: <https://netboxlabs.com/docs/netbox/en/stable/configuration/>
+- Example configuration: <https://github.com/netbox-community/netbox/blob/main/netbox/netbox/configuration_example.py>
+
+## Web Server Setup
+
+As this role just sets up gunicorn, but doesn't set up a web server, you need to do that yourself.  
+The relevant documentation on how to do that can be found here:
+
+- Web server setup docs: <https://netboxlabs.com/docs/netbox/en/stable/installation/5-http-server/>
+- Example base nginx config: <https://github.com/netbox-community/netbox/blob/main/contrib/nginx.conf>
+
+## Links & Resources
+
+- The NetBox Git Repo: <https://github.com/netbox-community/netbox>
+- The NetBox installation docs: <https://netboxlabs.com/docs/netbox/en/stable/installation/>
diff --git a/roles/netbox/handlers/main.yaml b/roles/netbox/handlers/main.yaml
new file mode 100644
index 0000000..fd7eb62
--- /dev/null
+++ b/roles/netbox/handlers/main.yaml
@@ -0,0 +1,24 @@
+- name: Run upgrade script
+  ansible.builtin.command: /opt/netbox/upgrade.sh
+  become: true
+  # When it runs, this should always report changed.
+  changed_when: true
+
+- name: Ensure netbox systemd services are set up and up-to-date
+  ansible.builtin.systemd_service:
+    daemon_reload: true
+    name: "{{ item }}"
+    enabled: true
+    state: restarted
+  become: true
+  loop:
+    - "netbox.service"
+    - "netbox-rq.service"
+
+- name: Ensure netbox housekeeping timer is set up and up-to-date
+  ansible.builtin.systemd_service:
+    daemon_reload: true
+    name: "netbox-housekeeping.timer"
+    enabled: true
+    state: restarted
+  become: true
diff --git a/roles/netbox/meta/argument_specs.yaml b/roles/netbox/meta/argument_specs.yaml
new file mode 100644
index 0000000..f836b43
--- /dev/null
+++ b/roles/netbox/meta/argument_specs.yaml
@@ -0,0 +1,12 @@
+argument_specs:
+  main:
+    options:
+      netbox__version:
+        type: str
+        required: true
+      netbox__db_password:
+        type: str
+        required: true
+      netbox__config:
+        type: str
+        required: true
diff --git a/roles/netbox/meta/main.yaml b/roles/netbox/meta/main.yaml
new file mode 100644
index 0000000..79c845d
--- /dev/null
+++ b/roles/netbox/meta/main.yaml
@@ -0,0 +1,11 @@
+---
+dependencies:
+  - role: redis
+  - role: postgresql
+    vars:
+      postgresql__dbs:
+        - name: netbox
+          owner: netbox
+      postgresql__users:
+        - name: netbox
+          password: "{{ netbox__db_password }}"
diff --git a/roles/netbox/tasks/main.yaml b/roles/netbox/tasks/main.yaml
new file mode 100644
index 0000000..89bf4a6
--- /dev/null
+++ b/roles/netbox/tasks/main.yaml
@@ -0,0 +1,103 @@
+- name: Ensure all dependencies are installed
+  ansible.builtin.apt:
+    name:
+      - python3
+      - python3-pip
+      - python3-venv
+      - python3-dev
+      - build-essential
+      - libxml2-dev
+      - libxslt1-dev
+      - libffi-dev
+      - libpq-dev
+      - libssl-dev
+      - zlib1g-dev
+      - git
+  become: true
+
+- name: Ensure NetBox source is present
+  ansible.builtin.git:
+    repo: https://github.com/netbox-community/netbox.git
+    dest: /opt/netbox/
+    version: "{{ netbox__version }}"
+  become: true
+  notify:
+    - Run upgrade script
+    - Ensure netbox systemd services are set up and up-to-date
+
+- name: Ensure netbox user
+  block:
+    - name: Ensure netbox group exists
+      ansible.builtin.group:
+        name: netbox
+        system: true
+      become: true
+
+    - name: Ensure netbox user exists
+      ansible.builtin.user:
+        name: netbox
+        group: netbox
+        password: '!'
+        system: true
+      become: true
+
+- name: Ensure relevant directories are owned by netbox user
+  ansible.builtin.file:
+    path: "{{ item }}"
+    state: directory
+    owner: netbox
+    recurse: true
+  become: true
+  loop:
+    - "/opt/netbox/netbox/media/"
+    - "/opt/netbox/netbox/reports/"
+    - "/opt/netbox/netbox/scripts/"
+
+- name: Deploy configuration.py
+  ansible.builtin.copy:
+    content: "{{ netbox__config }}"
+    dest: "/opt/netbox/netbox/netbox/configuration.py"
+    mode: "0644"
+    owner: root
+    group: root
+  become: true
+  notify: Ensure netbox systemd services are set up and up-to-date
+
+- name: Ensure provided gunicorn config is copied
+  ansible.builtin.copy:
+    remote_src: true
+    src: "/opt/netbox/contrib/gunicorn.py"
+    dest: "/opt/netbox/gunicorn.py"
+    mode: "0644"
+    owner: root
+    group: root
+  become: true
+  notify: Ensure netbox systemd services are set up and up-to-date
+
+- name: Ensure provided netbox systemd service files are copied
+  ansible.builtin.copy:
+    remote_src: true
+    src: "/opt/netbox/contrib/{{ item }}"
+    dest: "/etc/systemd/system/{{ item }}"
+    mode: "0644"
+    owner: root
+    group: root
+  become: true
+  loop:
+    - "netbox.service"
+    - "netbox-rq.service"
+  notify: Ensure netbox systemd services are set up and up-to-date
+
+- name: Ensure provided housekeeping systemd service and timer are copied
+  ansible.builtin.copy:
+    remote_src: true
+    src: "/opt/netbox/contrib/{{ item }}"
+    dest: "/etc/systemd/system/{{ item }}"
+    mode: "0644"
+    owner: root
+    group: root
+  become: true
+  loop:
+    - "netbox-housekeeping.service"
+    - "netbox-housekeeping.timer"
+  notify: Ensure netbox housekeeping timer is set up and up-to-date