diff --git a/.gitmodules b/.gitmodules
index cdf46d4..9408e63 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,3 +7,6 @@
[submodule "states/dmarc_reports/dmarcts-report-viewer"]
path = states/dmarc_reports/dmarcts-report-viewer
url = https://github.com/techsneeze/dmarcts-report-viewer.git
+[submodule "formulas/nextcloud-formula"]
+ path = formulas/nextcloud-formula
+ url = https://github.com/saltstack-formulas/nextcloud-formula.git
diff --git a/formulas/nextcloud-formula b/formulas/nextcloud-formula
new file mode 160000
index 0000000..1472638
--- /dev/null
+++ b/formulas/nextcloud-formula
@@ -0,0 +1 @@
+Subproject commit 147263851b84a19a831a9887b67fc800a452dda3
diff --git a/pillar/hostinfo/cloud.sls b/pillar/hostinfo/cloud.sls
new file mode 100644
index 0000000..ae35978
--- /dev/null
+++ b/pillar/hostinfo/cloud.sls
@@ -0,0 +1,3 @@
+hostinfo:
+ hostname: cloud
+ hostname_fqdn: cloud.lunch.org.uk
diff --git a/pillar/nextcloud/cloud.sls b/pillar/nextcloud/cloud.sls
new file mode 100644
index 0000000..b2f7bb2
--- /dev/null
+++ b/pillar/nextcloud/cloud.sls
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+# vim: ft=yaml
+---
+nextcloud:
+ lookup:
+ # Just for testing purposes
+ winner: lookup
+ added_in_lookup: lookup_value
+
+ php_executable: /usr/bin/php
+ rootgroup: root
+
+ # Default: webroot + '/data'
+ # datadir: /var/www/nextcloud/data
+ webuser: www-data
+ webroot: /srv/nextcloud
+ # nextcloud/ in some installations
+ websubdir: false
+
+ # Installation
+ # archive or pkg
+ install_mode: archive
+ archive:
+ name: latest-32
+ pkg:
+ name: nextcloud
+
+ # The defaults from 'occ maintenance:install'
+ database:
+ driver: mysql
+ name: nextcloud
+ host: localhost
+ port: 3306
+ user: nextcloud@localhost
+ pass: prayer-monkey-breeze
+ # driver: sqlite
+ # name: nextcloud
+ # Not relevant for sqlite
+ # host: localhost
+ # port: 5432
+ # user: nextcloud
+ # pass: password
+ # table-prefix: oc_
+ # table-space:
+ initial_admin_credentials:
+ user: admin
+ pass: mystic-pizza-antartic-sledge
+ # pass: password
+ email: webmaster@lunch.org.uk
+
+ salt_managed_config:
+ 'htaccess.RewriteBase': '/'
+ appcodechecker: true
+ updatechecker: true
+ 'memcache.local': '\OC\Memcache\APCu'
+
+ tofs:
+ # The files_switch key serves as a selector for alternative
+ # directories under the formula files directory. See TOFS pattern
+ # doc for more info.
+ # Note: Any value not evaluated by `config.get` will be used literally.
+ # This can be used to set custom paths, as many levels deep as required.
+ files_switch:
+ - any/path/can/be/used/here
+ - id
+ - roles
+ - osfinger
+ - os
+ - os_family
+ # All aspects of path/file resolution are customisable using the options below.
+ # This is unnecessary in most cases; there are sensible defaults.
+ # Default path: salt://< path_prefix >/< dirs.files >/< dirs.default >
+ # I.e.: salt://nextcloud/files/default
+ # path_prefix: template_alt
+ # dirs:
+ # files: files_alt
+ # default: default_alt
+ # The entries under `source_files` are prepended to the default source files
+ # given for the state
+ # source_files:
+ # nextcloud-config-file-file-managed:
+ # - 'example_alt.tmpl'
+ # - 'example_alt.tmpl.jinja'
+
+ # For testing purposes
+ source_files:
+ nextcloud-config-file-file-managed:
+ - 'example.tmpl.jinja'
+ nextcloud-subcomponent-config-file-file-managed:
+ - 'subcomponent-example.tmpl.jinja'
+
+ ##
+ # nextcloud.apache
+ # Apache config snippet
+ #apache:
+ # config_snippet: /etc/apache2/nextcloud-snippet.conf
+ # Assumes you're using apache-formula
+ # defaults to true
+ # trigger_reload: false
+
+ # Just for testing purposes
+ #winner: pillar
+ #added_in_pillar: pillar_value
diff --git a/pillar/top.sls b/pillar/top.sls
index adf1b7f..54f142f 100644
--- a/pillar/top.sls
+++ b/pillar/top.sls
@@ -20,3 +20,8 @@ base:
- hostinfo/social
- secrets/certificates
- secrets/restic
+ 'cloud.hollowfurlong.lunch.org.uk':
+ - hostinfo/cloud
+ - secrets/certificates
+ - secrets/mariadb
+ - nextcloud/cloud
diff --git a/states/cloud/cloud.lunch.org.uk.conf b/states/cloud/cloud.lunch.org.uk.conf
new file mode 100644
index 0000000..730f722
--- /dev/null
+++ b/states/cloud/cloud.lunch.org.uk.conf
@@ -0,0 +1,64 @@
+
+
+
+ ServerName cloud.lunch.org.uk
+
+ ErrorLog /var/log/apache2/cloud-error.log
+ CustomLog /var/log/apache2/cloud-access.log combined
+
+ RemoteIPProxyProtocol On
+
+ ProxyFCGIBackendType FPM
+
+ DocumentRoot /srv/nextcloud
+
+
+ Options +FollowSymLinks
+ AllowOverride All
+ Order allow,deny
+ Allow from all
+ Require all granted
+
+
+ Dav off
+
+
+ SetEnv HOME /srv/nextcloud
+ SetEnv HTTP_HOME /srv/nextcloud
+
+
+
+ SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
+
+
+
+ # HSTS (mod_headers is required) (15768000 seconds = 6 months)
+ Header always set Strict-Transport-Security "max-age=15768000"
+ # No embedding the site. Anti-clickjacking credentials.
+ Header set Content-Security-Policy "frame-ancestors 'none';"
+
+
+ SSLEngine on
+ SSLCertificateFile /var/local/certificates/cloud.lunch.org.uk/fullchain.pem
+ SSLCertificateKeyFile /var/local/certificates/cloud.lunch.org.uk/privkey.pem
+
+
+
+
+
+ ServerName cloud.lunch.org.uk
+
+ ErrorLog /var/log/apache2/cloud-error.log
+ CustomLog /var/log/apache2/cloud-access.log combined
+
+ RemoteIPProxyProtocol On
+
+
+ #
+ # This redirects all accesses to the HTTPS version of the site.
+ #
+ RewriteEngine On
+
+ RewriteRule ^/?(.*) https://cloud.lunch.org.uk/$1 [R=301,L]
+
+
diff --git a/states/cloud/init.sls b/states/cloud/init.sls
new file mode 100644
index 0000000..e1d3cc1
--- /dev/null
+++ b/states/cloud/init.sls
@@ -0,0 +1,49 @@
+cloud_pkgs:
+ pkg.installed:
+ - pkgs:
+ - memcached
+ - php-apcu
+ - php-memcached
+ - php-mysql
+ - php-gd
+ - php-json
+ - php-curl
+ - php-mbstring
+ - php-intl
+ - php-imagick
+ - php-xml
+ - php-zip
+
+cloud_mysql_user:
+ mysql_user.present:
+ - name: '{{ salt['pillar.get']('nextcloud:database:user', '') }}'
+ - password: '{{ salt['pillar.get']('nextcloud:database:pass', '') }}'
+ mysql_database.present:
+ - name: '{{ salt['pillar.get']('nextcloud:database:name', '') }}'
+ mysql_grants.present:
+ - grant: all privileges
+ - database: '{{ salt['pillar.get']('nextcloud:database:name', '') }}.*'
+ - user: '{{ salt['pillar.get']('nextcloud:database:user', '') }}'
+
+include:
+ - nextcloud
+
+cloud_web:
+ file.managed:
+ - require:
+ - sls: apache
+ - sls: certificates/client
+ - sls: php_fpm/trixie
+ - sls: nextcloud
+ - names:
+ - /etc/apache2/sites-available/cloud.lunch.org.uk.conf:
+ - source: salt://cloud/cloud.lunch.org.uk.conf
+ apache_site.enabled:
+ - require:
+ - file: /etc/apache2/sites-available/cloud.lunch.org.uk.conf
+ - name: cloud.lunch.org.uk
+ service.running:
+ - name: apache2
+ - reload: true
+ - watch:
+ - file: /etc/apache2/sites-available/cloud.lunch.org.uk.conf
diff --git a/states/firewalls/cloud.sls b/states/firewalls/cloud.sls
new file mode 100644
index 0000000..17a8d23
--- /dev/null
+++ b/states/firewalls/cloud.sls
@@ -0,0 +1,13 @@
+social_public:
+ firewalld.present:
+ - name: public
+ - default: False
+ - services:
+ - dhcpv6-client
+ - ssh
+ - http
+ - https
+ - prune_services: True
+ - require:
+ - firewalld
+
diff --git a/states/php_fpm/trixie.sls b/states/php_fpm/trixie.sls
new file mode 100644
index 0000000..3fb0256
--- /dev/null
+++ b/states/php_fpm/trixie.sls
@@ -0,0 +1,18 @@
+php-fpm:
+ pkg.installed
+
+apache_php_module:
+ apache_module.disabled:
+ - name: php8.4
+
+apache_fcgi_module:
+ apache_module.enabled:
+ - name: proxy_fcgi
+
+apache_setenvif_module:
+ apache_module.enabled:
+ - name: setenvif
+
+apache_php8.4_conf:
+ apache_conf.enabled:
+ - name: php8.4-fpm
diff --git a/states/top.sls b/states/top.sls
index 6165280..ded59c5 100644
--- a/states/top.sls
+++ b/states/top.sls
@@ -48,3 +48,15 @@ base:
- firewalls/social
- backup/restic
- activitypub
+
+ 'cloud.hollowfurlong.lunch.org.uk':
+ - debian/trixie
+ - certificates/client
+ - email-satellite
+ - fail2ban
+ - firewalls/cloud
+ - mariadb
+ - apache
+ - php_fpm/trixie
+ - cloud
+