diff --git a/.gitignore b/.gitignore
new file mode 100755
index 0000000..723ef36
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.idea
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
old mode 100644
new mode 100755
diff --git a/defaults/main.yml b/defaults/main.yml
new file mode 100755
index 0000000..89e0a63
--- /dev/null
+++ b/defaults/main.yml
@@ -0,0 +1,3 @@
+---
+# harden by default
+harden_os: true
diff --git a/example.docker-compose.yml b/example.docker-compose.yml
new file mode 100644
index 0000000..76a6635
--- /dev/null
+++ b/example.docker-compose.yml
@@ -0,0 +1,81 @@
+version: '2'
+
+services:
+  rocketchat:
+    image: registry.rocket.chat/rocketchat/rocket.chat:latest
+    command: >
+      bash -c
+        "for i in `seq 1 30`; do
+          node main.js &&
+          s=$$? && break || s=$$?;
+          echo \"Tried $$i times. Waiting 5 secs...\";
+          sleep 5;
+        done; (exit $$s)"
+    restart: unless-stopped
+    volumes:
+      - ./uploads:/app/uploads
+    environment:
+      - PORT=3000
+      - ROOT_URL=http://localhost:3000
+      - MONGO_URL=mongodb://mongo:27017/rocketchat
+      - MONGO_OPLOG_URL=mongodb://mongo:27017/local
+      - REG_TOKEN=${REG_TOKEN}
+#       - MAIL_URL=smtp://smtp.email
+#       - HTTP_PROXY=http://proxy.domain.com
+#       - HTTPS_PROXY=http://proxy.domain.com
+    depends_on:
+      - mongo
+    ports:
+      - 3000:3000
+    labels:
+      - "traefik.backend=rocketchat"
+      - "traefik.frontend.rule=Host: your.domain.tld"
+
+  mongo:
+    image: mongo:4.0
+    restart: unless-stopped
+    volumes:
+     - ./data/db:/data/db
+     #- ./data/dump:/dump
+    command: mongod --smallfiles --oplogSize 128 --replSet rs0 --storageEngine=mmapv1
+    labels:
+      - "traefik.enable=false"
+
+  # this container's job is just run the command to initialize the replica set.
+  # it will run the command and remove himself (it will not stay running)
+  mongo-init-replica:
+    image: mongo:4.0
+    command: >
+      bash -c
+        "for i in `seq 1 30`; do
+          mongo mongo/rocketchat --eval \"
+            rs.initiate({
+              _id: 'rs0',
+              members: [ { _id: 0, host: 'localhost:27017' } ]})\" &&
+          s=$$? && break || s=$$?;
+          echo \"Tried $$i times. Waiting 5 secs...\";
+          sleep 5;
+        done; (exit $$s)"
+    depends_on:
+      - mongo
+
+  #traefik:
+  #  image: traefik:latest
+  #  restart: unless-stopped
+  #  command: >
+  #    traefik
+  #     --docker
+  #     --acme=true
+  #     --acme.domains='your.domain.tld'
+  #     --acme.email='your@email.tld'
+  #     --acme.entrypoint=https
+  #     --acme.storagefile=acme.json
+  #     --defaultentrypoints=http
+  #     --defaultentrypoints=https
+  #     --entryPoints='Name:http Address::80 Redirect.EntryPoint:https'
+  #     --entryPoints='Name:https Address::443 TLS.Certificates:'
+  #  ports:
+  #    - 80:80
+  #    - 443:443
+  #  volumes:
+  #    - /var/run/docker.sock:/var/run/docker.sock
diff --git a/meta/main.yml b/meta/main.yml
new file mode 100644
index 0000000..009f249
--- /dev/null
+++ b/meta/main.yml
@@ -0,0 +1,12 @@
+---
+dependencies: []
+
+galaxy_info:
+  author: "Brandon Shipley"
+  description: "Ansible role to install/run rocket chat via docker-compose on a hardened Ubuntu 20.04 server"
+  company: "none"
+  license: MIT
+  min_anisble_version: 2.9
+  role_name: ansible-rocketchat-role
+  galaxy_tags: 
+    - rocketchat
diff --git a/tasks/docker.yml b/tasks/docker.yml
new file mode 100644
index 0000000..b6af838
--- /dev/null
+++ b/tasks/docker.yml
@@ -0,0 +1,26 @@
+---
+
+# install docker using geerlingguy docker role
+- name: 'Use geerlingguy.docker role'
+  include_role:
+    name: ansible-role-docker
+  tags: docker
+
+- name: 'Use geerlingguy.pip role to install docker via pip'
+  vars:
+    pip_install_packages:
+      - name: docker
+      - name: docker-compose
+
+  include_role:
+    name: ansible-role-pip
+  tags: docker
+
+- name: Add adminstrator to docker group
+  user:
+    name: "{{ main_user }}"
+    groups: docker
+    append: yes
+
+- name: reset ssh connection to allow user changes to affect 'current login user'
+  meta: reset_connection
diff --git a/tasks/harden.yml b/tasks/harden.yml
new file mode 100644
index 0000000..a6a9b28
--- /dev/null
+++ b/tasks/harden.yml
@@ -0,0 +1,11 @@
+---
+
+# harden the ubuntu server via ubuntu2004_cis
+- name: 'Use ubuntu2004_cis role'
+  include_role:
+    name: ubuntu2004_cis
+  tags: harden
+
+- name: 'Include fail2ban/install using apt'
+  apt: name=fail2ban state=latest update_cache=yes force_apt_get=yes
+  tags: harden
diff --git a/tasks/main.yml b/tasks/main.yml
new file mode 100644
index 0000000..80a9198
--- /dev/null
+++ b/tasks/main.yml
@@ -0,0 +1,20 @@
+---
+# tasks file for setting up an assetto server on ubuntu20.04
+- include: ssh_port_fallback.yml
+- include: harden.yml
+#  become: true
+#  apply tags,become
+#  when: harden_os
+#  tags: harden
+
+- include: docker.yml
+#  become: true
+#  apply tags,become
+#  when: harden_os
+#  tags: harden
+
+- include: rocketchat.yml
+#  become: true
+#  apply tags,become
+#  when: harden_os
+#  tags: harden
diff --git a/tasks/rocketchat.yml b/tasks/rocketchat.yml
new file mode 100644
index 0000000..fdc5f16
--- /dev/null
+++ b/tasks/rocketchat.yml
@@ -0,0 +1,60 @@
+---
+- name: Install unzip using apt
+  apt: name=unzip state=latest update_cache=yes force_apt_get=yes
+
+- name: "NOTSCORED | 3.5.1.6 | PATCH | Ensure firewall rules exist for all open ports"
+  ufw:
+    rule: allow
+    proto: tcp
+    port: "{{ item }}"
+  loop:
+    - '3000'
+    - '80'
+    - '443'
+    - '22'
+
+- name: Creates directory structure for assetto content
+  file:
+    path: /home/{{ main_user }}/rocketchat/data
+    state: directory
+    owner: "{{ main_user }}"
+    group: "{{ main_user }}"
+    mode: 0775
+
+- name: bring down rocketchat docker-compose
+  become_user: "{{ main_user }}"
+  docker_compose:
+    project_src: /home/{{ main_user }}/rocketchat/
+    state: absent
+  register: __remove_rocketchat
+  tags:
+    - bring-down
+
+- name: update permissions
+  file:
+    path: /home/{{ main_user }}
+    state: directory
+    recurse: yes
+    owner: "{{ main_user }}"
+    group: "{{ main_user }}"
+    mode: 0775
+
+- name: setup nginx reverse proxy from template
+  template:
+    src: templates/rocketchat/nginx.conf.j2
+    dest: /home/{{ main_user }}/rocketchat/nginx/nginx.conf
+
+- name: docker compose up
+  become_user: "{{ main_user }}"
+  docker_compose:
+    project_src: /home/{{ main_user }}/rocketchat/
+    state: present
+  register: __rocketchat
+
+- name: debug docker compose down
+  debug:
+    var: __remove_rocketchat
+
+- name: debug docker compose up debug
+  debug:
+    var: __rocketchat
diff --git a/tasks/ssh_port_fallback.yml b/tasks/ssh_port_fallback.yml
new file mode 100644
index 0000000..785e53e
--- /dev/null
+++ b/tasks/ssh_port_fallback.yml
@@ -0,0 +1,55 @@
+---
+#
+# https://gist.github.com/triplepoint/1ad6c6060c0f12112403d98180bcf0b4
+#
+# This task list is intended to be imported by playbooks, before any
+# other tasks are performed.  It lets us determine whether the configured SSH
+# port is available, and lets us fall back to the default port if necessary.
+#
+# The use case here is when a role in the playbook is configured to change the
+# sshd port, but the first time the role is executed the host is still
+# listening on the default port.  With this check in place, we can fall back
+# to the default port on the first run, and then on subsequent runs use the
+# configured port.
+#
+# Be advised that running this task list in a `gather_facts: false` state as
+# required means simple failures can go unexplained.  For example, if python2
+# is not available, the `wait_for_connection` calls will just time out without
+# explanation.
+#
+# Execute these tasks as the first thing in a playbook like so:
+# - hosts: some-host-group
+#   gather_facts: false
+#   tasks:
+#     - import_tasks: _sshd_port_juggling.yml
+
+- name: SSH Port Juggle | define the fallback default SSH port
+  set_fact:
+    _default_ssh_port: 22
+
+- name: SSH Port Juggle | Try configured ansible_port {{ ansible_port }}
+  wait_for_connection:
+    timeout: 10
+  ignore_errors: true
+  register: _ssh_port_result
+
+- name: SSH Port Juggle | Set the ansible_port to the fallback default port {{ _default_ssh_port }}
+  set_fact:
+    ansible_port: "{{ _default_ssh_port }}"
+  when:
+    - _ssh_port_result is failed
+
+- name: SSH Port Juggle | Check fallback default port {{ ansible_port }}
+  wait_for_connection:
+    timeout: 10
+  ignore_errors: true
+  register: _ssh_port_default_result
+  when:
+    - _ssh_port_result is failed
+
+- name: SSH Port Juggle | Fail
+  fail: msg="Neither the configured ansible_port {{ ansible_port }} nor the fallback port {{ _default_ssh_port }} were reachable"
+  when:
+    - _ssh_port_result is failed
+    - _ssh_port_default_result is defined
+    - _ssh_port_default_result is failed
diff --git a/templates/nginx.conf.j2 b/templates/nginx.conf.j2
new file mode 100755
index 0000000..6169922
--- /dev/null
+++ b/templates/nginx.conf.j2
@@ -0,0 +1,35 @@
+# Upstreams
+upstream backend {
+    server 127.0.0.1:3000;
+}
+
+# HTTPS Server
+server {
+    listen 443;
+    server_name {{ rocketchat_hostname }};
+
+    # You can increase the limit if your need to.
+    client_max_body_size 200M;
+
+    error_log /var/log/nginx/rocketchat.access.log;
+
+    ssl on;
+    ssl_certificate /etc/nginx/rocketchat.crt;
+    ssl_certificate_key /etc/nginx/rocketchat.key;
+    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # don’t use SSLv3 ref: POODLE
+
+    location / {
+        proxy_pass http://backend;
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection "upgrade";
+        proxy_set_header Host $http_host;
+
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto https;
+        proxy_set_header X-Nginx-Proxy true;
+
+        proxy_redirect off;
+    }
+}
\ No newline at end of file