locals { nextcloud_env_vars = { # Database configuration MYSQL_DATABASE = "nextcloud" MYSQL_HOST = module.nextcloud_database.host # Email configuration SMTP_HOST = local.scaleway_smtp_host SMTP_SECURE = "ssl" SMTP_PORT = 465 MAIL_FROM_ADDRESS = "cloud" MAIL_DOMAIN = "alexpires.me" SMTP_AUTHTYPE = "PLAIN" # Redis configuration REDIS_HOST = "redis" # Object as primary storage OBJECTSTORE_S3_HOST = "s3.${scaleway_object_bucket.nextcloud.region}.scw.cloud" OBJECTSTORE_S3_BUCKET = scaleway_object_bucket.nextcloud.name OBJECTSTORE_S3_PORT = 443 OBJECTSTORE_S3_SSL = true OBJECTSTORE_S3_REGION = scaleway_object_bucket.nextcloud.region OBJECTSTORE_S3_AUTOCREATE = false # General NEXTCLOUD_TRUSTED_DOMAINS = local.nextcloud_fqdn TRUSTED_PROXIES = join(" ", ["10.1.0.0/16"]) OVERWRITEHOST = local.nextcloud_fqdn OVERWRITEPROTOCOL = "https" OVERWRITECLIURL = "https://${local.nextcloud_fqdn}" } nextcloud_secrets = { OBJECTSTORE_S3_KEY = { key = "access_key" name = kubernetes_secret.nextcloud_object_store_credentials.metadata[0].name } OBJECTSTORE_S3_SECRET = { key = "secret_key" name = kubernetes_secret.nextcloud_object_store_credentials.metadata[0].name } SMTP_NAME = { key = "username" name = kubernetes_secret.nextcloud_mail_credentials.metadata[0].name } SMTP_PASSWORD = { key = "password" name = kubernetes_secret.nextcloud_mail_credentials.metadata[0].name } MYSQL_USER = { key = "username" name = module.nextcloud_database.app_crendentials_name } MYSQL_PASSWORD = { key = "password" name = module.nextcloud_database.app_crendentials_name } NEXTCLOUD_ADMIN_USER = { key = "username" name = kubernetes_secret.nextcloud_admin_credentials.metadata[0].name } NEXTCLOUD_ADMIN_PASSWORD = { key = "password" name = kubernetes_secret.nextcloud_admin_credentials.metadata[0].name } } nextcloud_path = "/mnt/microk8s_persistent/nextcloud" nextcloud_mysql_path = "/mnt/microk8s_persistent/mysql.nextcloud" } # # Namespace # resource "kubernetes_namespace" "nextcloud" { metadata { annotations = { name = "nextcloud" } labels = { name = "nextcloud" } name = "nextcloud" } } # # Nextcloud required credentials # resource "kubernetes_secret" "nextcloud_mail_credentials" { metadata { name = "nextcloud-mail-credentials" namespace = kubernetes_namespace.nextcloud.metadata[0].name } data = { username = var.scaleway_project_id password = scaleway_iam_api_key.mail_app_api.secret_key } type = "Opaque" } resource "kubernetes_secret" "nextcloud_object_store_credentials" { metadata { name = "nextcloud-object-store-credentials" namespace = kubernetes_namespace.nextcloud.metadata[0].name } data = { access_key = scaleway_iam_api_key.nextcloud_app_api.access_key secret_key = scaleway_iam_api_key.nextcloud_app_api.secret_key } type = "Opaque" } resource "kubernetes_secret" "nextcloud_admin_credentials" { metadata { name = "nextcloud-admin-credentials" namespace = kubernetes_namespace.nextcloud.metadata[0].name } data = { username = "admin" password = var.nextcloud_admin_password } type = "Opaque" } # # Setup MariaDB database # module "nextcloud_database" { source = "./modules/k8s_mariadb" namespace = kubernetes_namespace.nextcloud.metadata[0].name app_name = "nextcloud" app_backup_folder = "nextcloud.backups" app_password = var.nextcloud_mysql_password root_password = var.mysql_root_password retention_days = local.local_backup_retention_days cron_schedule = local.nextcloud_sql_backup_cron_schedule host_storage_path = local.nextcloud_mysql_path } # # Setup rsync backup job to scaleway bucket # module "nextcloud_backup" { source = "./modules/scw_backup_job" namespace = kubernetes_namespace.nextcloud.metadata[0].name app_name = "nextcloud" app_files_folder = "nextcloud" app_backup_folder = "mysql.nextcloud/nextcloud.backups" scaleway_organization_id = var.scaleway_organization_id scaleway_project_id = var.scaleway_organization_id access_key = scaleway_iam_api_key.backup_app_api.access_key secret_key = scaleway_iam_api_key.backup_app_api.secret_key bucket_name = scaleway_object_bucket.backup.name retention_days = local.local_backup_retention_days cron_schedule = local.nextcloud_files_backup_cron_schedule } # # Nextcloud service, ingress, deployment and file backup job # # resource "kubernetes_service_account" "nextcloud_service_account" { metadata { name = "nextcloud-app-sa" namespace = kubernetes_namespace.nextcloud.metadata[0].name } automount_service_account_token = false } resource "kubernetes_service" "nextcloud_redis" { metadata { name = "redis" namespace = kubernetes_namespace.nextcloud.metadata[0].name } spec { port { port = 6379 name = "redis" } selector = { app = "redis" } cluster_ip = "None" } } resource "kubernetes_service" "nextcloud_apache" { metadata { name = "http" namespace = kubernetes_namespace.nextcloud.metadata[0].name } spec { port { port = 80 target_port = "http" } selector = { app = "nextcloud" } cluster_ip = "None" } } resource "kubernetes_ingress_v1" "nextcloud_ingress" { depends_on = [ kubernetes_deployment.nextcloud_apache ] metadata { name = "nextcloud-ingress" namespace = kubernetes_namespace.nextcloud.metadata[0].name annotations = { "kubernetes.io/ingress.class" = "public" "cert-manager.io/cluster-issuer" = "letsencrypt-prod" "kubernetes.io/tls-acme" = "true" "nginx.ingress.kubernetes.io/ssl-redirect" = "true" "nginx.ingress.kubernetes.io/proxy-body-size" = "4G" "nginx.ingress.kubernetes.io/proxy-request-buffering" = "on" "nginx.ingress.kubernetes.io/proxy-buffer-size" = "32k" "nginx.ingress.kubernetes.io/proxy-buffers-number" = "16" "nginx.ingress.kubernetes.io/server-snippet" = file("${path.module}/resources/nextcloud-server-snippet.conf") } } spec { tls { hosts = [local.nextcloud_fqdn] secret_name = "nextcloud-tls" } rule { host = local.nextcloud_fqdn http { path { backend { service { name = "http" port { number = 80 } } } } } } } } resource "kubernetes_deployment" "nextcloud_redis" { metadata { name = "redis" namespace = kubernetes_namespace.nextcloud.metadata[0].name } spec { selector { match_labels = { app = "redis" } } strategy { type = "RollingUpdate" } template { metadata { labels = { app = "redis" } } spec { container { name = "redis" image = "redis:latest" resources { requests = { memory = "20Mi" } limits = { memory = "50Mi" } } security_context { allow_privilege_escalation = false } readiness_probe { tcp_socket { port = "redis" } initial_delay_seconds = 10 period_seconds = 15 success_threshold = 1 failure_threshold = 3 } liveness_probe { tcp_socket { port = "redis" } initial_delay_seconds = 10 period_seconds = 15 success_threshold = 1 failure_threshold = 3 } port { container_port = 6379 name = "redis" protocol = "TCP" } } } } } } resource "kubernetes_deployment" "nextcloud_apache" { depends_on = [ module.nextcloud_database, kubernetes_deployment.nextcloud_redis ] metadata { name = "nextcloud-app" namespace = kubernetes_namespace.nextcloud.metadata[0].name } spec { selector { match_labels = { app = "nextcloud" } } strategy { type = "Recreate" } template { metadata { labels = { app = "nextcloud" } } spec { service_account_name = kubernetes_service_account.nextcloud_service_account.metadata[0].name container { name = "nextcloud" image = "nextcloud:${local.nextcloud_version}" image_pull_policy = "Always" resources { requests = { memory = "200Mi" } } readiness_probe { http_get { port = "http" path = "/status.php" } initial_delay_seconds = 30 period_seconds = 10 success_threshold = 1 failure_threshold = 3 } liveness_probe { tcp_socket { port = "http" } initial_delay_seconds = 5 period_seconds = 10 success_threshold = 1 failure_threshold = 3 } dynamic "env" { for_each = local.nextcloud_env_vars content { name = env.key value = env.value } } security_context { allow_privilege_escalation = false } dynamic "env" { for_each = local.nextcloud_secrets content { name = env.key value_from { secret_key_ref { key = env.value.key name = env.value.name } } } } port { container_port = 80 name = "http" } volume_mount { mount_path = "/var/www/html" name = "persistent-storage" } } volume { name = "persistent-storage" host_path { path = local.nextcloud_path } } } } } lifecycle { replace_triggered_by = [ kubernetes_deployment.nextcloud_redis ] } } resource "kubernetes_cron_job_v1" "nextcloud_cron_apache" { depends_on = [ kubernetes_deployment.nextcloud_apache ] metadata { namespace = kubernetes_namespace.nextcloud.metadata[0].name name = "cronjob-trigger" } spec { schedule = "*/15 * * * *" concurrency_policy = "Forbid" failed_jobs_history_limit = 2 successful_jobs_history_limit = 2 job_template { metadata { labels = { app = "cronjob-trigger" } } spec { backoff_limit = 1 parallelism = 1 completions = 1 active_deadline_seconds = 300 template { metadata { labels = { app = "cronjob-trigger" } } spec { container { name = "nextcloud" image = "nextcloud:${local.nextcloud_version}" command = ["curl"] args = ["--fail", "-L", "http://http/cron.php"] security_context { allow_privilege_escalation = false } } restart_policy = "Never" termination_grace_period_seconds = 30 } } } } } lifecycle { replace_triggered_by = [ kubernetes_deployment.nextcloud_apache ] } }