diff --git a/nextcloud/defaults/main.yml b/nextcloud/defaults/main.yml
new file mode 100644
index 0000000..d302d73
--- /dev/null
+++ b/nextcloud/defaults/main.yml
@@ -0,0 +1,5 @@
+site_conf: cloud.conf
+php_version: "8.4"
+mysql_db_name: nextcloud
+mysql_db_user: nextcloud
+web_root: "/var/www/nextcloud"
diff --git a/nextcloud/handlers/main.yml b/nextcloud/handlers/main.yml
new file mode 100644
index 0000000..af398e8
--- /dev/null
+++ b/nextcloud/handlers/main.yml
@@ -0,0 +1,5 @@
+---
+- name: restart apache
+ service:
+ name: apache2
+ state: restarted
diff --git a/nextcloud/tasks/apache.yml b/nextcloud/tasks/apache.yml
new file mode 100644
index 0000000..5cc6b76
--- /dev/null
+++ b/nextcloud/tasks/apache.yml
@@ -0,0 +1,35 @@
+- name: Set hostname
+ ansible.builtin.hostname:
+ name: "{{ hostname }}"
+
+- name: "Enable recommended Apache Modules."
+ apache2_module: "name={{ item }} state=present"
+ with_items:
+ - dir
+ - env
+ - headers
+ - mime
+ - rewrite
+ - setenvif
+ notify: restart apache
+
+- name: Add Apache virtualhost for Nextcloud
+ template:
+ src: "templates/{{ site_conf }}.j2"
+ dest: "/etc/apache2/sites-available/{{ site_conf }}"
+ owner: root
+ group: root
+ mode: 0644
+ notify: restart apache
+
+- name: Enable the Nextcloud site.
+ command: >
+ a2ensite {{ site_conf }}
+ creates="/etc/apache2/sites-enabled/{{ site_conf }}"
+ notify: restart apache
+
+- name: Disable the default site.
+ command: >
+ a2dissite 000-default
+ removes=/etc/apache2/sites-enabled/000-default.conf
+ notify: restart apache
diff --git a/nextcloud/tasks/dependencies.yml b/nextcloud/tasks/dependencies.yml
new file mode 100644
index 0000000..320532c
--- /dev/null
+++ b/nextcloud/tasks/dependencies.yml
@@ -0,0 +1,72 @@
+---
+- name: Get software for apt repository management.
+ apt:
+ state: present
+ name:
+ - python3-apt
+ - python3-pycurl
+ - python3-pymysql
+ - gnupg2
+
+#- name: Add ondrej repository for later versions of PHP.
+# apt_repository:
+# repo: "ppa:ondrej/php"
+# update_cache: yes
+
+#sudo dpkg -l | grep php | tee packages.txt
+#sudo apt install apt-transport-https lsb-release ca-certificates wget -y
+#sudo wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
+#sudo sh -c 'echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
+#sudo apt update
+
+- name: "Install Apache, MySQL, PHP, and other dependencies."
+ apt:
+ state: present
+ name:
+ - acl
+ - git
+ - curl
+ - wget
+ - unzip
+ - openssl
+ - redis-server
+ - mariadb-server
+ - libpcre3-dev
+ - apache2
+ - "libapache2-mod-php"
+ - "php{{ php_version }}"
+ - "php{{ php_version }}-imagick"
+ - "php{{ php_version }}-common"
+ - "php{{ php_version }}-curl"
+ - "php{{ php_version }}-gd"
+ - "php{{ php_version }}-imap"
+ - "php{{ php_version }}-intl"
+ #- "php{{ php_version }}-json"
+ - "php{{ php_version }}-mbstring"
+ - "php{{ php_version }}-gmp"
+ - "php{{ php_version }}-bcmath"
+ - "php{{ php_version }}-mysql"
+ - "php{{ php_version }}-ssh2"
+ - "php{{ php_version }}-xml"
+ - "php{{ php_version }}-zip"
+ - "php{{ php_version }}-apcu"
+ - "php{{ php_version }}-redis"
+ - "php{{ php_version }}-ldap"
+ #- "php{{ php_version }}-smbclient"
+ - php-phpseclib
+ - bzip2
+ - rsync
+ - jq
+ - inetutils-ping
+ - ldap-utils
+ - smbclient
+ - cron
+
+#- name: Disable the firewall (since this is behind a firewall)
+# service: name=ufw state=stopped
+
+- name: "Start Apache, MySQL, and PHP."
+ service: "name={{ item }} state=started enabled=yes"
+ with_items:
+ - apache2
+ - mysql
diff --git a/nextcloud/tasks/main.yml b/nextcloud/tasks/main.yml
new file mode 100644
index 0000000..7dce4ad
--- /dev/null
+++ b/nextcloud/tasks/main.yml
@@ -0,0 +1,24 @@
+---
+- name: Install LAMP stack dependencies
+ include_tasks:
+ file: dependencies.yml
+
+- name: Configure Apache.
+ include_tasks:
+ file: apache.yml
+
+- name: Configure PHP.
+ include_tasks:
+ file: php.yml
+
+- name: Configure MySQL.
+ include_tasks:
+ file: mysql.yml
+
+- name: Create occ helper script.
+ include_tasks:
+ file: occ.yml
+
+- name: Download Nextcloud.
+ include_tasks:
+ file: nextcloud.yml
diff --git a/nextcloud/tasks/mysql.yml b/nextcloud/tasks/mysql.yml
new file mode 100644
index 0000000..db06d08
--- /dev/null
+++ b/nextcloud/tasks/mysql.yml
@@ -0,0 +1,16 @@
+- name: Create a MySQL database.
+ community.mysql.mysql_db:
+ name: "{{ mysql_db_name }}"
+ state: present
+ login_unix_socket: /run/mysqld/mysqld.sock
+
+- name: Create a MySQL db user.
+ community.mysql.mysql_user:
+ name: "{{ mysql_db_user }}"
+ password: "{{ mysql_passwd }}"
+ login_user: "root"
+ login_password: "{{ mysql_passwd }}"
+ priv: "{{ mysql_db_user }}.*:ALL"
+ host: localhost
+ state: present
+ login_unix_socket: /run/mysqld/mysqld.sock
diff --git a/nextcloud/tasks/nextcloud.yml b/nextcloud/tasks/nextcloud.yml
new file mode 100644
index 0000000..c78af24
--- /dev/null
+++ b/nextcloud/tasks/nextcloud.yml
@@ -0,0 +1,13 @@
+---
+- name: Download Nextcloud source.
+ ansible.builtin.get_url:
+ url: https://download.nextcloud.com/server/releases/latest.tar.bz2
+ dest: "/tmp/nextcloud-complete-latest.tar.bz2"
+ owner: www-data
+
+- name: Extract the archive.
+ ansible.builtin.unarchive:
+ src: "/tmp/nextcloud-complete-latest.tar.bz2"
+ dest: "/var/www/"
+ owner: www-data
+ remote_src: yes
diff --git a/nextcloud/tasks/occ.yml b/nextcloud/tasks/occ.yml
new file mode 100644
index 0000000..f53ad67
--- /dev/null
+++ b/nextcloud/tasks/occ.yml
@@ -0,0 +1,7 @@
+- name: Create a helper script for running occ commands.
+ template:
+ src: "templates/occ.j2"
+ dest: "/usr/local/bin/occ"
+ owner: root
+ group: root
+ mode: 0755
diff --git a/nextcloud/tasks/php.yml b/nextcloud/tasks/php.yml
new file mode 100644
index 0000000..dbcab3f
--- /dev/null
+++ b/nextcloud/tasks/php.yml
@@ -0,0 +1,16 @@
+---
+- name: Adjust OpCache memory setting.
+ lineinfile:
+ dest: "/etc/php/{{ php_version }}/apache2/conf.d/10-opcache.ini"
+ regexp: "^opcache.memory_consumption"
+ line: "opcache.memory_consumption = 96"
+ state: present
+ notify: restart apache
+
+#- name: Adjust smbclient setting.
+# template:
+# src: "templates/smbclient.ini.j2"
+# dest: "/etc/php/7.4/mods-available/smbclient.ini"
+# owner: root
+# group: root
+# notify: restart apache
diff --git a/nextcloud/templates/cloud.conf.j2 b/nextcloud/templates/cloud.conf.j2
new file mode 100644
index 0000000..f87a0b6
--- /dev/null
+++ b/nextcloud/templates/cloud.conf.j2
@@ -0,0 +1,17 @@
+
+ServerName {{ hostname }}.{{ domain_base }}
+DirectoryIndex index.php index.html
+DocumentRoot {{ web_root }}
+
+ Options FollowSymLinks MultiViews
+ AllowOverride All
+ Require all granted
+
+
+ Dav off
+
+
+ #SetEnv HOME {{ web_root }}
+ #SetEnv HTTP_HOME {{ web_root }}
+
+
diff --git a/nextcloud/templates/occ.j2 b/nextcloud/templates/occ.j2
new file mode 100644
index 0000000..20f6ed4
--- /dev/null
+++ b/nextcloud/templates/occ.j2
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+cd /var/www/nextcloud || exit
+sudo -E -u www-data /usr/bin/php /var/www/nextcloud/occ "$@"
diff --git a/smtp_nextcloud/handlers/main.yml b/smtp_nextcloud/handlers/main.yml
new file mode 100644
index 0000000..8c305d0
--- /dev/null
+++ b/smtp_nextcloud/handlers/main.yml
@@ -0,0 +1,4 @@
+- name: Reload postfix
+ service:
+ name: postfix
+ state: reloaded
diff --git a/smtp_nextcloud/tasks/main.yml b/smtp_nextcloud/tasks/main.yml
new file mode 100644
index 0000000..da40e76
--- /dev/null
+++ b/smtp_nextcloud/tasks/main.yml
@@ -0,0 +1,23 @@
+---
+- name: Perform a dist-upgrade.
+ ansible.builtin.apt:
+ upgrade: dist
+ update_cache: yes
+
+- name: Set mailname
+ template:
+ src: templates/mailname
+ dest: /etc/mailname
+
+- name: Install postfix
+ package:
+ name:
+ - postfix
+ - mailutils
+ state: present
+
+- name: Copy the config file
+ template:
+ src: templates/main.cf
+ dest: /etc/postfix/main.cf
+ notify: Reload postfix
diff --git a/smtp_nextcloud/templates/header_check b/smtp_nextcloud/templates/header_check
new file mode 100644
index 0000000..17f22d3
--- /dev/null
+++ b/smtp_nextcloud/templates/header_check
@@ -0,0 +1 @@
+/From:.*/ REPLACE From: Nextcloud
diff --git a/smtp_nextcloud/templates/mailname b/smtp_nextcloud/templates/mailname
new file mode 100644
index 0000000..8eced49
--- /dev/null
+++ b/smtp_nextcloud/templates/mailname
@@ -0,0 +1 @@
+{{ hostname }}.{{ domain_base }}
diff --git a/smtp_nextcloud/templates/main.cf b/smtp_nextcloud/templates/main.cf
new file mode 100644
index 0000000..c2b80cf
--- /dev/null
+++ b/smtp_nextcloud/templates/main.cf
@@ -0,0 +1,44 @@
+# See /usr/share/postfix/main.cf.dist for a commented, more complete version
+
+
+# Debian specific: Specifying a file name will cause the first
+# line of that file to be used as the name. The Debian default
+# is /etc/mailname.
+myorigin = /etc/mailname
+myhostname = {{ hostname }}.{{ domain_base }}
+recipient_delimiter = +
+inet_interfaces = all
+inet_protocols = all
+
+smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
+biff = no
+
+# appending .domain is the MUA's job.
+append_dot_mydomain = no
+
+# Uncomment the next line to generate "delayed mail" warnings
+#delay_warning_time = 4h
+
+readme_directory = no
+
+# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 3.6 on
+# fresh installs.
+compatibility_level = 3.6
+
+# TLS parameters
+smtp_tls_CApath=/etc/ssl/certs
+smtp_tls_security_level=may
+smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
+
+
+alias_maps = hash:/etc/aliases
+alias_database = hash:/etc/aliases
+mydestination = localhost
+relayhost =
+mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
+mailbox_size_limit = 0
+
+## Enforce correct sender address
+sender_canonical_classes = envelope_sender, header_sender
+sender_canonical_maps = regexp:/etc/postfix/sender_canonical
+smtp_header_checks = regexp:/etc/postfix/header_check
diff --git a/smtp_nextcloud/templates/sender_canonical b/smtp_nextcloud/templates/sender_canonical
new file mode 100644
index 0000000..90d1b14
--- /dev/null
+++ b/smtp_nextcloud/templates/sender_canonical
@@ -0,0 +1 @@
+/.+/ nextcloud@{{ hostname }}.{{ domain_base }}