diff --git a/ansible/roles/apps/tasks/cert-manager.yml b/ansible/roles/apps/tasks/cert-manager.yml
index b7ff8e286cf75c104f2f75353b29bec4e8b08616..6ad32349443ae3d59ff40a76d2690f54395cd054 100644
--- a/ansible/roles/apps/tasks/cert-manager.yml
+++ b/ansible/roles/apps/tasks/cert-manager.yml
@@ -45,16 +45,18 @@
     - name: production
       server: "https://acme-v02.api.letsencrypt.org/directory"
 
-- name: Install cert-manager
+- name: Create Kubernetes secret with cert-manager settings
   tags:
-    - helmfile
+    - config
+    - flux
     - cert-manager
-  include_role:
-    name: "helmfile"
-    tasks_from: "apply"
-    apply:
-      tags:
-        - helmfile
-        - cert-manager
-  vars:
-      helmfile: '05-cert-manager'
+  k8s:
+    state: present
+    definition:
+      api_version: v1
+      kind: Secret
+      metadata:
+        namespace: "oas"
+        name: "cert-manager-settings"
+      data:
+        values.yaml: "{{ lookup('template','cert-manager-values.yaml') | b64encode }}"
diff --git a/ansible/roles/apps/tasks/local-storage.yml b/ansible/roles/apps/tasks/local-storage.yml
index 7c5c8a86109fb7c8005aa8f81a1f105ffa032a47..8b01f5bf0a5d8ac7f6d797325c917163f30b6b6c 100644
--- a/ansible/roles/apps/tasks/local-storage.yml
+++ b/ansible/roles/apps/tasks/local-storage.yml
@@ -1,24 +1,17 @@
 ---
-- name: Clone local-storage repo
-  tags:
-    - git
-    - helmfile
-    - local-storage
-  git:
-    repo: 'https://open.greenhost.net/openappstack/local-storage'
-    dest: '{{ data_directory }}/source/repos/local-storage'
-    version: '{{ git_local_storage_version }}'
 
-- name: Install local-storage provisioner
+- name: Create Kubernetes secret with local-storage values
   tags:
-    - helmfile
+    - config
+    - flux
     - local-storage
-  include_role:
-    name: "helmfile"
-    tasks_from: "apply"
-    apply:
-      tags:
-        - helmfile
-        - local-storage
-  vars:
-      helmfile: '00-storage'
+  k8s:
+    state: present
+    definition:
+      api_version: v1
+      kind: Secret
+      metadata:
+        namespace: "oas"
+        name: "local-storage-settings"
+      data:
+        values.yaml: "{{ lookup('template','local-storage-values.yaml') | b64encode }}"
diff --git a/ansible/roles/apps/tasks/main.yml b/ansible/roles/apps/tasks/main.yml
index 10de1819a8f9b1c23c40f549f10e2262c94f2207..47f0e897ff29c498725569fea5d061d8fb80d5b1 100644
--- a/ansible/roles/apps/tasks/main.yml
+++ b/ansible/roles/apps/tasks/main.yml
@@ -8,10 +8,8 @@
   tags: [ helmfile ]
   when: '"00-flux" in helmfiles'
 
-- name: Install local-storage
+- name: Perform tasks necessary for local-storage
   import_tasks: local-storage.yml
-  tags: [ helmfile ]
-  when: '"00-storage" in helmfiles'
 
 - name: Install cert-manager
   import_tasks: cert-manager.yml
diff --git a/ansible/roles/apps/tasks/nextcloud.yml b/ansible/roles/apps/tasks/nextcloud.yml
index 1b6047647c34a46d0a7f88d6fc044f3266f783ea..af3bdc5788ede5c437acb4f1ec05cac436020ab9 100644
--- a/ansible/roles/apps/tasks/nextcloud.yml
+++ b/ansible/roles/apps/tasks/nextcloud.yml
@@ -1,10 +1,9 @@
 ---
 
-- name: Create Kubernetes secret with NextCloud values
+- name: Create Kubernetes secret with NextCloud settings
   tags:
     - config
     - flux
-    - oas
     - nextcloud
   k8s:
     state: present
@@ -13,6 +12,6 @@
       kind: Secret
       metadata:
         namespace: "oas-apps"
-        name: "oas"
+        name: "nextcloud-settings"
       data:
-        nextcloud.yaml: "{{ lookup('template','secrets.nextcloud.yaml') | b64encode }}"
+        values.yaml: "{{ lookup('template','nextcloud-values.yaml') | b64encode }}"
diff --git a/ansible/roles/apps/tasks/nginx.yml b/ansible/roles/apps/tasks/nginx.yml
index a3a60176429f6227ee84a7b75d4b81a704b8e298..a84dd71746cb6727dac73ae062baf57b25d7dad8 100644
--- a/ansible/roles/apps/tasks/nginx.yml
+++ b/ansible/roles/apps/tasks/nginx.yml
@@ -1,14 +1,17 @@
 ---
-- name: Install nginx ingress controller
+
+- name: Create Kubernetes secret with nginx-ingress settings
   tags:
-    - helmfile
+    - config
+    - flux
     - nginx
-  include_role:
-    name: "helmfile"
-    tasks_from: "apply"
-    apply:
-      tags:
-        - helmfile
-        - nginx
-  vars:
-      helmfile: '10-nginx'
+  k8s:
+    state: present
+    definition:
+      api_version: v1
+      kind: Secret
+      metadata:
+        namespace: "oas"
+        name: "ingress-settings"
+      data:
+        values.yaml: "{{ lookup('template','ingress-values.yaml') | b64encode }}"
diff --git a/ansible/roles/apps/tasks/prometheus.yml b/ansible/roles/apps/tasks/prometheus.yml
index 9d4f800114c62132a6820dec7b2a18a486a36ed5..9866fa72611a751a0552d8d0d13f2d08db00ef97 100644
--- a/ansible/roles/apps/tasks/prometheus.yml
+++ b/ansible/roles/apps/tasks/prometheus.yml
@@ -31,21 +31,19 @@
     recurse: true
   when: prometheus_pv_name.stdout
 
-- name: Install prometheus and grafana
-  include_role:
-    name: "helmfile"
-    tasks_from: "apply"
-    apply:
-      tags:
-        - monitoring
-        - prometheus
-      environment:
-        - GRAFANA_ADMIN_PASSWORD: "{{ grafana_admin_password }}"
+- name: Create Kubernetes secret with monitoring settings
   tags:
-    - helmfile
+    - config
+    - flux
+    - monitoring
     - prometheus
-  vars:
-      helmfile: '15-monitoring'
-      # Force needed for upgrading from 5 to 6, see
-      # https://github.com/helm/charts/tree/master/stable/prometheus-operator#upgrading-from-5xx-to-6xx
-      helmfile_apply_args: '--args="--force"'
+  k8s:
+    state: present
+    definition:
+      api_version: v1
+      kind: Secret
+      metadata:
+        namespace: "oas"
+        name: "monitoring-settings"
+      data:
+        values.yaml: "{{ lookup('template','monitoring-values.yaml') | b64encode }}"
diff --git a/helmfiles/values/cert-manager.yaml.gotmpl b/ansible/roles/apps/templates/cert-manager-values.yaml
similarity index 61%
rename from helmfiles/values/cert-manager.yaml.gotmpl
rename to ansible/roles/apps/templates/cert-manager-values.yaml
index 1e3749018de49c1e8ff4ca4c3ded45339b74bb2f..2b6e7e67c92450dde421657e5ba57ee57c568ccb 100644
--- a/helmfiles/values/cert-manager.yaml.gotmpl
+++ b/ansible/roles/apps/templates/cert-manager-values.yaml
@@ -1,7 +1,7 @@
 ingressShim:
-  {{- if .Environment.Values.acmeStaging | default false }}
+  {% if acme_staging %}
   defaultIssuerName: "letsencrypt-staging"
-  {{- else }}
+  {% else %}
   defaultIssuerName: "letsencrypt-production"
-  {{- end }}
+  {% endif %}
   defaultIssuerKind: ClusterIssuer
diff --git a/helmfiles/values/nginx.yaml.gotmpl b/ansible/roles/apps/templates/ingress-values.yaml
similarity index 54%
rename from helmfiles/values/nginx.yaml.gotmpl
rename to ansible/roles/apps/templates/ingress-values.yaml
index e4bfa621df0280f6420e42fcf853b6d9730eebd2..3bd8c554621d92dceaceb07c4dddd4845d88fbcc 100644
--- a/helmfiles/values/nginx.yaml.gotmpl
+++ b/ansible/roles/apps/templates/ingress-values.yaml
@@ -1,9 +1,6 @@
 controller:
-  # scope:
-  #   enabled: true
-  #   namespace: oas-apps
   service:
-    externalIPs: ["{{ .Environment.Values.ip }}"]
+    externalIPs: ["{{ ip_address }}"]
   resources:
     limits:
       cpu: 100m
diff --git a/helmfiles/values/local-storage.yaml b/ansible/roles/apps/templates/local-storage-values.yaml
similarity index 100%
rename from helmfiles/values/local-storage.yaml
rename to ansible/roles/apps/templates/local-storage-values.yaml
diff --git a/helmfiles/values/prometheus.yaml.gotmpl b/ansible/roles/apps/templates/monitoring-values.yaml
similarity index 91%
rename from helmfiles/values/prometheus.yaml.gotmpl
rename to ansible/roles/apps/templates/monitoring-values.yaml
index a0d2d2564badd6039e92c5c259213fd961a2e32e..1d4bf9f795d83a801b83713229ae4f3391d0b667 100644
--- a/helmfiles/values/prometheus.yaml.gotmpl
+++ b/ansible/roles/apps/templates/monitoring-values.yaml
@@ -92,7 +92,9 @@ additionalPrometheusRulesMap:
           severity: warning
       - alert: KubeQuotaExceeded
         annotations:
+          {% raw %}
           message: Namespace {{`{{ $labels.namespace }}`}} is using {{`{{ printf "%0.0f" $value }}`}}% of its {{`{{ $labels.resource }}`}} quota.
+          {% endraw %}
           runbook_url: https://github.com/kubernetes-monitoring/kubernetes-mixin/tree/master/runbook.md#alert-name-kubequotaexceeded
         expr: |-
           100 * kube_resourcequota{job="kube-state-metrics", type="used"}
@@ -104,7 +106,9 @@ additionalPrometheusRulesMap:
           severity: warning
       - alert: CPUThrottlingHigh
         annotations:
-          message: '{{`{{ printf "%0.0f" $value }}`}}% throttling of CPU in namespace {{`{{ $labels.namespace }}`}} for container {{`{{ $labels.container_name }}`}} in pod {{`{{ $labels.pod_name }}`}}.'
+          {% raw %}
+          message: '{{ printf "%0.0f" $value }}% throttling of CPU in namespace {{ $labels.namespace }} for container {{ $labels.container_name }} in pod {{ $labels.pod_name }}.'
+          {% endraw %}
           runbook_url: https://github.com/kubernetes-monitoring/kubernetes-mixin/tree/master/runbook.md#alert-name-cputhrottlinghigh
         expr: |-
           100 * sum(increase(container_cpu_cfs_throttled_periods_total{container_name!="", }[5m])) by (container_name, pod_name, namespace)
@@ -116,18 +120,18 @@ additionalPrometheusRulesMap:
           severity: warning
 
 grafana:
-  adminPassword: "{{ requiredEnv "GRAFANA_ADMIN_PASSWORD" }}"
+  adminPassword: "{{ grafana_admin_password }}"
 
   ingress:
     enabled: true
     annotations:
       kubernetes.io/tls-acme: "true"
     hosts:
-      - "grafana.{{ .Environment.Values.domain }}"
+      - "grafana.{{ domain }}"
     tls:
       - secretName: grafana-tls
         hosts:
-          - "grafana.{{ .Environment.Values.domain }}"
+          - "grafana.{{ domain }}"
   persistence:
     enabled: true
     size: 2Gi
diff --git a/ansible/roles/apps/templates/secrets.nextcloud.yaml b/ansible/roles/apps/templates/nextcloud-values.yaml
similarity index 100%
rename from ansible/roles/apps/templates/secrets.nextcloud.yaml
rename to ansible/roles/apps/templates/nextcloud-values.yaml
diff --git a/ansible/roles/local-flux/tasks/main.yml b/ansible/roles/local-flux/tasks/main.yml
index 00e5a4257ff302478c78db13ab37d3269de8550e..4bf719ff6a91e77be5a29e5a8d5616593fb10a93 100644
--- a/ansible/roles/local-flux/tasks/main.yml
+++ b/ansible/roles/local-flux/tasks/main.yml
@@ -66,7 +66,7 @@
   - name: Install local-flux helm chart
     tags:
       - flux
-    shell: helm install --namespace=oas --name=local-flux /var/lib/OpenAppStack/source/local-flux
+    shell: helm upgrade --install --namespace=oas local-flux /var/lib/OpenAppStack/source/local-flux
   
   vars:
     repo: "/var/lib/OpenAppStack/local-flux"
diff --git a/flux/cert-manager.yaml b/flux/cert-manager.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..84065665e792590467f17382c06d55d936474e20
--- /dev/null
+++ b/flux/cert-manager.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: helm.fluxcd.io/v1
+kind: HelmRelease
+metadata:
+  name: cert-manager
+  namespace: oas
+  annotations:
+    flux.weave.works/automated: "false"
+spec:
+  releaseName: cert-manager
+  chart:
+    repository: https://charts.jetstack.io
+    name: cert-manager
+    version: 0.11.0
+  valuesFrom:
+    - secretKeyRef:
+        name: cert-manager-settings
+        key: values.yaml
diff --git a/flux/local-storage.yaml b/flux/local-storage.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..977d151ebfede882791b1b8b925a904e25dae8f4
--- /dev/null
+++ b/flux/local-storage.yaml
@@ -0,0 +1,19 @@
+---
+apiVersion: helm.fluxcd.io/v1
+kind: HelmRelease
+metadata:
+  name: local-storage
+  namespace: oas
+  annotations:
+    flux.weave.works/automated: "false"
+spec:
+  releaseName: local-storage
+  chart:
+    git: https://open.greenhost.net/openappstack/local-storage
+    ref: master
+    path: .
+  valuesFrom:
+    - secretKeyRef:
+        name: local-storage-settings
+        key: values.yaml
+  timeout: 120
diff --git a/flux/monitoring.yaml b/flux/monitoring.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..f058885c2b924f3909b7e38aea63505d5c89c997
--- /dev/null
+++ b/flux/monitoring.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: helm.fluxcd.io/v1
+kind: HelmRelease
+metadata:
+  name: monitoring
+  namespace: oas
+  annotations:
+    flux.weave.works/automated: "false"
+spec:
+  releaseName: monitoring
+  chart:
+    repository: https://kubernetes-charts.storage.googleapis.com/
+    name: prometheus-operator
+    version: 7.4.0
+  valuesFrom:
+    - secretKeyRef:
+        name: monitoring-settings
+        key: values.yaml
diff --git a/flux/nextcloud.yaml b/flux/nextcloud.yaml
index 1df101280d86dcbdf5009d2ab99440bae05dc985..7a174b865bf257d31a7913f928756e76bfe48f83 100644
--- a/flux/nextcloud.yaml
+++ b/flux/nextcloud.yaml
@@ -16,6 +16,6 @@ spec:
     path: .
   valuesFrom:
     - secretKeyRef:
-        name: oas
-        key: nextcloud.yaml
+        name: nextcloud-settings
+        key: values.yaml
   timeout: 900
diff --git a/flux/nginx.yaml b/flux/nginx.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..079e3226d1c9a124464b256bf9b7d00b7bb10338
--- /dev/null
+++ b/flux/nginx.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: helm.fluxcd.io/v1
+kind: HelmRelease
+metadata:
+  name: ingress
+  namespace: oas
+  annotations:
+    flux.weave.works/automated: "false"
+spec:
+  releaseName: ingress
+  chart:
+    repository: https://kubernetes-charts.storage.googleapis.com/
+    name: nginx-ingress
+    version: 1.26.1
+  valuesFrom:
+    - secretKeyRef:
+        name: ingress-settings
+        key: values.yaml
diff --git a/helmfiles/helmfile.d/00-storage.yaml b/helmfiles/helmfile.d/00-storage.yaml
deleted file mode 100644
index f9ce001f0a3c66d7e2a3b3279d898f64084fd7f8..0000000000000000000000000000000000000000
--- a/helmfiles/helmfile.d/00-storage.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-environments:
-  oas:
-    values:
-      - "/etc/OpenAppStack/values/local.yaml"
-
-releases:
-  - name: "oas-{{ .Environment.Values.releaseName }}-local-storage"
-    namespace: "oas"
-    chart: "../../repos/local-storage/"
-    values:
-    - "../values/local-storage.yaml"
diff --git a/helmfiles/helmfile.d/05-cert-manager.yaml b/helmfiles/helmfile.d/05-cert-manager.yaml
deleted file mode 100644
index 94eb17daa7564a689cb7260952cd8891b6174ff3..0000000000000000000000000000000000000000
--- a/helmfiles/helmfile.d/05-cert-manager.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-environments:
-  oas:
-    values:
-      - "/etc/OpenAppStack/values/local.yaml"
-
-repositories:
-  - name: jetstack
-    url: https://charts.jetstack.io
-
-releases:
-  - name: "oas-{{ .Environment.Values.releaseName }}-cert-manager"
-    namespace: "cert-manager"
-    chart: "jetstack/cert-manager"
-    version: "0.11.0"
-    values:
-    - "../values/cert-manager.yaml.gotmpl"
-    wait: false
diff --git a/helmfiles/helmfile.d/10-nginx.yaml b/helmfiles/helmfile.d/10-nginx.yaml
deleted file mode 100644
index 3e89ef640fc8f824cf48c092352c8480266f5d11..0000000000000000000000000000000000000000
--- a/helmfiles/helmfile.d/10-nginx.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-environments:
-  oas:
-    values:
-      - "/etc/OpenAppStack/values/local.yaml"
-
-releases:
-  - name: "oas-{{ .Environment.Values.releaseName }}-proxy"
-    namespace: "oas"
-    chart: "stable/nginx-ingress"
-    values:
-    - "../values/nginx.yaml.gotmpl"
-    - "/etc/OpenAppStack/values/apps/nginx.yaml.gotmpl"
-    wait: false
diff --git a/helmfiles/helmfile.d/15-monitoring.yaml b/helmfiles/helmfile.d/15-monitoring.yaml
deleted file mode 100644
index a73e191cac33f9ff881cebb349ceae1bcbebe5c7..0000000000000000000000000000000000000000
--- a/helmfiles/helmfile.d/15-monitoring.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-environments:
-  oas:
-    values:
-      - "/etc/OpenAppStack/values/local.yaml"
-
-releases:
-  - name: "oas-{{ .Environment.Values.releaseName }}-prometheus"
-    namespace: "oas"
-    chart: "stable/prometheus-operator"
-    # NOTE: If you change this version, also update the crd_version number in
-    # ansible/group_vars/all/oas.yml
-    version: "7.4.0"
-    values:
-    - "../values/prometheus.yaml.gotmpl"
-    - "/etc/OpenAppStack/values/apps/prometheus.yaml.gotmpl"
-    wait: false
diff --git a/helmfiles/values/nextcloud.yaml.gotmpl b/helmfiles/values/nextcloud.yaml.gotmpl
deleted file mode 100644
index e0a84e681f3d24f78286b82336749a8bccd96ca0..0000000000000000000000000000000000000000
--- a/helmfiles/values/nextcloud.yaml.gotmpl
+++ /dev/null
@@ -1,100 +0,0 @@
-nextcloud:
-  # Set this to true to debug your nextcloud
-  debug: false
-
-  nextcloud:
-    host: "files.{{ .Environment.Values.domain }}"
-    password: "{{ requiredEnv "NEXTCLOUD_PASSWORD" }}"
-
-  ingress:
-    enabled: true
-    annotations:
-      # Tell cert-manager to automatically get a TLS certificate
-      kubernetes.io/tls-acme: "true"
-      # Set max body size high to allow big NextCloud uploads
-      nginx.ingress.kubernetes.io/proxy-body-size: 1G
-      nginx.ingress.kubernetes.io/server-snippet: |-
-        server_tokens off;
-        location = /robots.txt {
-          allow all;
-          log_not_found off;
-          access_log off;
-        }
-    hosts:
-      - "files.{{ .Environment.Values.domain }}"
-    tls:
-      - hosts:
-          - "files.{{ .Environment.Values.domain }}"
-        secretName: oas-{{ .Environment.Values.releaseName }}-files
-
-  # Use 2 GB of storage for NC storage (maybe make configurable later?)
-  persistence:
-    enabled: true
-    size: 2Gi
-
-  # Explicitly disable use of internal database
-  internalDatabase:
-    enabled: false
-
-  # Enable and configure MariaDB chart
-  mariadb:
-    db:
-      password: "{{ requiredEnv "NEXTCLOUD_MARIADB_PASSWORD" }}"
-    enabled: true
-    master:
-      persistence:
-        ## Enable PostgreSQL persistence using Persistent Volume Claims.
-        enabled: true
-        size: 512Mi
-    replication:
-      enabled: false
-    rootUser:
-      password: "{{ requiredEnv "NEXTCLOUD_MARIADB_ROOT_PASSWORD" }}"
-  livenessProbe:
-    initialDelaySeconds: 120
-    timeoutSeconds: 20
-    periodSeconds: 30
-    successThreshold: 1
-    failureThreshold: 6
-  readinessProbe:
-    initialDelaySeconds: 10
-    timeoutSeconds: 20
-    periodSeconds: 30
-    successThreshold: 1
-    failureThreshold: 3
-
-onlyoffice:
-  server_name: "office.{{ .Environment.Values.domain }}"
-  ingress:
-    enabled: true
-    annotations:
-      # Tell cert-manager to automatically get a TLS certificate
-      kubernetes.io/tls-acme: "true"
-    paths:
-      - "/"
-    hosts:
-      - "office.{{ .Environment.Values.domain }}"
-    tls:
-      - hosts:
-          - "office.{{ .Environment.Values.domain }}"
-        secretName: oas-{{ .Environment.Values.releaseName }}-office
-  jwtSecret: "{{ requiredEnv "ONLYOFFICE_JWT_SECRET" }}"
-  livenessProbe:
-    initialDelaySeconds: 120
-    timeoutSeconds: 20
-    periodSeconds: 30
-    successThreshold: 1
-    failureThreshold: 6
-  readinessProbe:
-    initialDelaySeconds: 10
-    timeoutSeconds: 20
-    periodSeconds: 30
-    successThreshold: 1
-    failureThreshold: 3
-
-postgresql:
-  postgresqlPassword: "{{ requiredEnv "ONLYOFFICE_POSTGRESQL_PASSWORD" }}"
-
-rabbitmq:
-  rabbitmq:
-    password: "{{ requiredEnv "ONLYOFFICE_RABBITMQ_PASSWORD" }}"