diff --git a/playbooks/apollo.yml b/playbooks/apollo.yml
index 8d4c4e6f3c7a6daa05d9eada6405fdd96d84cab4..18c810cd8d5adb43719e523ba9eb074f5c9c1aee 100644
--- a/playbooks/apollo.yml
+++ b/playbooks/apollo.yml
@@ -52,3 +52,4 @@
     - { role: zabbix-server, tags: ["zabbix", "zabbix-server"] }
     - { role: grafana, tags: ["grafana"] }
     - { role: archwiki, tags: ["archwiki"] }
+    - { role: conf.archlinux.org }
diff --git a/roles/conf.archlinux.org/defaults/main.yml b/roles/conf.archlinux.org/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c20e66abc5ee9eaea0a4840727b04998f2845c04
--- /dev/null
+++ b/roles/conf.archlinux.org/defaults/main.yml
@@ -0,0 +1,6 @@
+---
+
+conference_user: confweb
+conference_git: https://github.com/archlinux/conf.archlinux.org.git
+conference_domain: conf.archlinux.org
+conference_dir: /srv/http/{{ conference_domain }}
diff --git a/roles/conf.archlinux.org/tasks/main.yml b/roles/conf.archlinux.org/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8424867a65c92f3cd30be13e3d81a97782d877b0
--- /dev/null
+++ b/roles/conf.archlinux.org/tasks/main.yml
@@ -0,0 +1,36 @@
+---
+
+- name: install hugo, git
+  package: name=hugo,git state=present
+
+- name: create {{ conference_user }} user
+  user: name={{ conference_user }} shell=/bin/false home={{ conference_dir }} createhome=no
+
+- name: clone conf.archlinux.org website
+  git: >
+    repo={{ conference_git }}
+    dest="{{ conference_dir }}"
+    version=8fe1b86d2a13a8c8227027e310f1708c7cfc1c1a
+  register: release
+
+- name: fix home permissions
+  file: state=directory owner={{ conference_user }} group={{ conference_user }} path="{{ conference_dir }}"
+
+- name: generate conf.archlinux.org site
+  command: hugo
+  become_user: "{{conference_user}}"
+  args:
+    chdir: "{{conference_dir}}"
+  when: release.changed
+
+- name: create ssl cert
+  command: certbot certonly --email webmaster@archlinux.org --agree-tos --rsa-key-size 4096 --renew-by-default --webroot -w {{letsencrypt_validation_dir}} -d '{{ conference_domain }}' creates='/etc/letsencrypt/live/{{ conference_domain }}/fullchain.pem'
+
+- name: make nginx log dir
+  file: path=/var/log/nginx/{{ conference_domain }} state=directory owner=root group=root mode=0755
+
+- name: set up nginx
+  template: src=nginx.d.conf.j2 dest=/etc/nginx/nginx.d/conf.archlinux.org.conf owner=root group=root mode=0644
+  notify:
+    - reload nginx
+  tags: ['nginx']
diff --git a/roles/conf.archlinux.org/templates/nginx.d.conf.j2 b/roles/conf.archlinux.org/templates/nginx.d.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..8186a71c749b5d4c15e0c4b165b9642a8c7ebd05
--- /dev/null
+++ b/roles/conf.archlinux.org/templates/nginx.d.conf.j2
@@ -0,0 +1,41 @@
+server {
+    listen       80;
+    listen       [::]:80;
+    server_name  {{ conference_domain }};
+
+    access_log   /var/log/nginx/{{ conference_domain }}/access.log reduced;
+    error_log    /var/log/nginx/{{ conference_domain }}/error.log;
+
+    include snippets/letsencrypt.conf;
+
+    location / {
+        access_log off;
+        rewrite ^(.*) https://$server_name$1 permanent;
+    }
+}
+
+server {
+    listen       443 ssl http2;
+    listen       [::]:443 ssl http2;
+    server_name  {{ conference_domain }};
+
+    access_log   /var/log/nginx/{{ conference_domain }}/access.log reduced;
+    error_log    /var/log/nginx/{{ conference_domain }}/error.log;
+
+    ssl_certificate      /etc/letsencrypt/live/{{ conference_domain }}/fullchain.pem;
+    ssl_certificate_key  /etc/letsencrypt/live/{{ conference_domain }}/privkey.pem;
+    ssl_trusted_certificate /etc/letsencrypt/live/{{ conference_domain }}/chain.pem;
+
+    # Security headers
+    add_header X-Frame-Options "SAMEORIGIN" always;
+    add_header X-Xss-Protection "1; mode=block" always;
+    add_header Referrer-Policy "same-origin";
+    add_header Feature-Policy "geolocation 'none' ;midi 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; fullscreen 'none'; payment 'none';";
+    add_header Content-Security-Policy "default-src 'self';";
+    add_header X-Content-Type-Options "nosniff" always;
+
+    # Apply HSTS header again, since adding a header removes previous headers
+    add_header Strict-Transport-Security $hsts_header;
+
+    root {{ conference_dir }}/public;
+}