[Yandex Cloud documentation](../../../index.md) > [Yandex Compute Cloud](../../index.md) > [Tutorials](../index.md) > [Deploying Nextcloud integrated with Object Storage](index.md) > Deploying Nextcloud on a VM or VM group using Terraform

# Deploying Nextcloud integrated with Yandex Object Storage on a Compute Cloud VM or VM group with the help of Terraform

In this tutorial, you will connect an Object Storage [bucket](../../../storage/concepts/bucket.md) to a Nextcloud solution deployed on a Compute Cloud [VM instance](../../concepts/vm.md) with a database in a Yandex Managed Service for MySQL® [cluster](../../../managed-mysql/concepts/index.md). To ensure fault tolerance and redundancy of your Nextcloud infrastructure, you will scale Nextcloud across an [instance group](../../concepts/instance-groups/index.md) and implement [L7 load balancing](../../../application-load-balancer/concepts/application-load-balancer.md) with Yandex Application Load Balancer. In the fault-tolerant configuration, Nextcloud will be available via a domain name, for which a [TLS certificate](../../../certificate-manager/concepts/managed-certificate.md) will be issued in Yandex Certificate Manager.

{% note info %}

To implement a fault-tolerant Nextcloud configuration, you will need a domain for the L7 load balancer.

{% endnote %}

Fault-tolerant solution diagram:

![integrate-nextcloud](../../../_assets/tutorials/integrate-nextcloud/integrate-nextcloud.svg)

Where:
* `example.com`: Your domain for which a [certificate](../../../certificate-manager/concepts/managed-certificate.md) is issued in Yandex Certificate Manager, connected to the [L7 load balancer](../../../application-load-balancer/concepts/application-load-balancer.md).
* `nextcloud-alb`: L7 load balancer to evenly distribute incoming user traffic across instance group hosts.
* `nextcloud-instance-group`: [Instance group](../../concepts/instance-groups/index.md) with hosts the Nextcloud solution is deployed on.
* `nextcloud-db-cluster`: Yandex Managed Service for MySQL® [cluster](../../../managed-mysql/concepts/index.md) with the Nextcloud service database.
* `my-nextcloud-bucket`: Yandex Object Storage [bucket](../../../storage/concepts/bucket.md) connected to the Nextcloud solution.

To deploy Nextcloud in Yandex Cloud and connect an Object Storage bucket:

1. [Get your cloud ready](#before-you-begin).
1. [Deploy Nextcloud in a basic configuration](#the-basic-variant):
    1. [Create an infrastructure for the basic configuration](#create-basic-infrastructure).
    1. [Install and configure Nextcloud on the VM](#setup-nextcloud).
    1. [Test the solution in the basic configuration](#test-simple).
1. [Deploy Nextcloud in a fault-tolerant configuration](#the-redundant-variant):
    1. [Complete Nextcloud setup](#tune-nextcloud).
    1. [Scale the infrastructure for the fault-tolerant configuration](#create-failsafe-infrastructure).
    1. [Test the solution in the fault-tolerant configuration](#test-redundant).

If you no longer need the resources you created, [delete them](#clear-out).

## Getting started {#before-you-begin}

Sign up for Yandex Cloud and create a [billing account](../../../billing/concepts/billing-account.md):
1. Navigate to the [management console](https://console.yandex.cloud) and log in to Yandex Cloud or create a new account.
1. On the **[Yandex Cloud Billing](https://center.yandex.cloud/billing/accounts)** page, make sure you have a billing account linked and it has the `ACTIVE` or `TRIAL_ACTIVE` [status](../../../billing/concepts/billing-account-statuses.md). If you do not have a billing account, [create one](../../../billing/quickstart/index.md) and [link](../../../billing/operations/pin-cloud.md) a cloud to it.

If you have an active billing account, you can create or select a [folder](../../../resource-manager/concepts/resources-hierarchy.md#folder) for your infrastructure on the [cloud page](https://console.yandex.cloud/cloud).

[Learn more about clouds and folders here](../../../resource-manager/concepts/resources-hierarchy.md).

### Required paid resources {#paid-resources}

The cost of the proposed solution includes: 
* Fee for [disks](../../concepts/disk.md), [disk snapshots](../../concepts/snapshot.md), and continuously running [VMs](../../concepts/vm.md) (see [Yandex Compute Cloud pricing](../../pricing.md)).
* Fee for using [public IP addresses](../../../vpc/concepts/address.md#public-addresses) and [NAT gateways](../../../vpc/concepts/gateways.md#nat-gateway) (see [Yandex Virtual Private Cloud pricing](../../../vpc/pricing.md)).
* Fee for [data storage](../../../storage/concepts/bucket.md) in Object Storage and data [operations](../../../storage/operations/index.md) (see [Yandex Object Storage pricing](../../../storage/pricing.md)).
* Fee for using a MySQL® managed DB (see [Managed Service for MySQL® pricing](../../../managed-mysql/pricing.md)).
* If using Yandex Cloud DNS, fee for [DNS zones](../../../dns/concepts/dns-zone.md#public-zones) and public DNS queries (see [Cloud DNS pricing](../../../dns/pricing.md)).
* If using a load balancer, fee for the number of [L7 load balancer](../../../application-load-balancer/concepts/application-load-balancer.md) resource units (see [Yandex Application Load Balancer pricing](../../../application-load-balancer/pricing.md)).
* If using a [log group](../../../logging/concepts/log-group.md) for load balancer logging, fee for data logging and storage (see [Yandex Cloud Logging pricing](../../../logging/pricing.md)).

## Deploy Nextcloud in a basic configuration {#the-basic-variant}

You will deploy the basic Nextcloud configuration on a single VM with the Nextcloud service database created in a single-host Managed Service for MySQL® cluster.

### Create an infrastructure for the basic configuration {#create-basic-infrastructure}

With [Terraform](https://www.terraform.io/), you can quickly create a cloud infrastructure in Yandex Cloud and manage it using configuration files. These files store the infrastructure description written in HashiCorp Configuration Language (HCL). If you change the configuration files, Terraform automatically detects which part of your configuration is already deployed, and what should be added or removed.

Terraform is distributed under the [Business Source License](https://github.com/hashicorp/terraform/blob/main/LICENSE). The [Yandex Cloud provider for Terraform](https://github.com/yandex-cloud/terraform-provider-yandex) is distributed under the [MPL-2.0](https://www.mozilla.org/en-US/MPL/2.0/) license.

For more information about the provider resources, see the relevant documentation on the [Terraform](https://www.terraform.io/docs/providers/yandex/index.html) website or [its mirror](../../../terraform/index.md).

To create an infrastructure using Terraform:
1. [Install Terraform](../../../tutorials/infrastructure-management/terraform-quickstart.md#install-terraform), [get the credentials](../../../tutorials/infrastructure-management/terraform-quickstart.md#get-credentials), and specify the source for installing Yandex Cloud (see [Configure your provider](../../../tutorials/infrastructure-management/terraform-quickstart.md#configure-provider), step 1).
1. Prepare your infrastructure description files:

    {% list tabs group=infrastructure_description %}

    - Ready-made configuration {#ready}

      1. Clone the repository with configuration files.

         ```bash
         git clone https://github.com/yandex-cloud-examples/yc-nextcloud-s3.git
         ```

      1. Navigate to the repository directory. Make sure it contains the following files and folders:
          * `nextcloud-integrate-storage-basic-config.tf`: Configuration file of the new basic infrastructure.
          * `nextcloud-integrate-storage.auto.tfvars`: File with the values of user-defined variables.
          * `failsafe`: Folder containing the fault-tolerant configuration:
              * `nextcloud-integrate-storage-failsafe-config.tf`: Configuration file of the new infrastructure in the fault-tolerant version.

    - Manually {#manual}

      1. Create a folder for configuration files.
      1. In the folder, create:

          1. `nextcloud-integrate-storage-basic-config.tf` configuration file:

              {% cut "nextcloud-integrate-storage-basic-config.tf" %}

              ```hcl
              # Declaring variables
              
              variable "folder_id" {
                type = string
              }
              
              variable "bucket_name" {
                type = string
              }
              
              variable "ssh_key_path" {
                type = string
              }
              
              variable "db_password" {
                type = string
                sensitive  = true
              }
              
              variable "domain_name" {
                type = string
              }
              
              locals {
                sa_name             = "nextcloud-sa"
                network_name        = "nextcloud-network"
                subnet_a_name       = "nextcloud-subnet-ru-central1-a"
                subnet-a-cidr       = "192.168.11.0/24"
                sg_name             = "nextcloud-sg"
                gw_name             = "nextcloud-gateway"
                vm_name             = "nextcloud-vm"
                vm_image_id         = "fd8vsghfu10ev5gdatkh"
                ig_name             = "nexcloud-ig"
                cluster_name        = "nextcloud-db-cluster"
                db_name             = "nextcloud"
                db_username         = "user"
              }
              
              # Configuring the provider
              
              terraform {
                required_providers {
                  yandex = {
                    source = "yandex-cloud/yandex"
                  }
                }
                required_version = ">= 0.13"
              }
              
              provider "yandex" {
                folder_id = var.folder_id
              }
              
              # Creating a network
              
              resource "yandex_vpc_network" "nextcloud-network" {
                name = local.network_name
              }
              
              # Creating a NAT gateway
              
              resource "yandex_vpc_gateway" "nextcloud-gw" {
                name = local.gw_name
                shared_egress_gateway {}
              }
              
              # Creating a routing table
              
              resource "yandex_vpc_route_table" "nextcloud-rt-table" {
                network_id  = yandex_vpc_network.nextcloud-network.id
              
                static_route {
                  destination_prefix = "0.0.0.0/0"
                  gateway_id         = yandex_vpc_gateway.nextcloud-gw.id
                }
              }
              
              # Creating a subnet
              
              resource "yandex_vpc_subnet" "nextcloud-subnet-a" {
                name           = local.subnet_a_name
                zone           = "ru-central1-a"
                network_id     = yandex_vpc_network.nextcloud-network.id
                v4_cidr_blocks = [local.subnet-a-cidr]
                route_table_id = yandex_vpc_route_table.nextcloud-rt-table.id
              }
              
              # Creating a security group
              
              resource "yandex_vpc_security_group" "nextcloud-sg" {
                name        = local.sg_name
                network_id  = yandex_vpc_network.nextcloud-network.id
              
                ingress {
                  protocol       = "TCP"
                  description    = "http"
                  v4_cidr_blocks = ["0.0.0.0/0"]
                  port           = 80
                }
              
                ingress {
                  protocol       = "TCP"
                  description    = "https"
                  v4_cidr_blocks = ["0.0.0.0/0"]
                  port           = 443
                }
              
                ingress {
                  protocol       = "TCP"
                  description    = "ssh"
                  v4_cidr_blocks = ["0.0.0.0/0"]
                  port           = 22
                }
              
                ingress {
                  protocol       = "ANY"
                  description    = "self"
                  predefined_target = "self_security_group"
                  from_port      = 0
                  to_port        = 65535
                }
              
                ingress {
                  protocol       = "ANY"
                  description    = "healthchecks"
                  predefined_target = "loadbalancer_healthchecks"
                  from_port      = 0
                  to_port        = 65535
                }
              
                egress {
                  protocol       = "ANY"
                  description    = "any"
                  v4_cidr_blocks = ["0.0.0.0/0"]
                  from_port      = 0
                  to_port        = 65535
                }
              }
              
              # Creating a service account
              
              resource "yandex_iam_service_account" "nextcloud-sa" {
                name = local.sa_name
              }
              
              # Assigning roles to a service account
              
              resource "yandex_resourcemanager_folder_iam_member" "editor" {
                folder_id = var.folder_id
                role      = "editor"
                member    = "serviceAccount:${yandex_iam_service_account.nextcloud-sa.id}"
              }
              
              # Creating a static access key for CA
              
              resource "yandex_iam_service_account_static_access_key" "sa-static-key" {
                service_account_id = yandex_iam_service_account.nextcloud-sa.id
              }
              
              # Creating a bucket
              
              resource "yandex_storage_bucket" "nextcloud-bucket" {
                access_key = yandex_iam_service_account_static_access_key.sa-static-key.access_key
                secret_key = yandex_iam_service_account_static_access_key.sa-static-key.secret_key
                bucket     = var.bucket_name
              }
              
              # Creating a boot disk for the VM
              
              resource "yandex_compute_disk" "boot-disk" {
                type     = "network-ssd"
                zone     = "ru-central1-a"
                size     = "24"
                image_id = local.vm_image_id
              }
              
              # Creating a VM
              
              resource "yandex_compute_instance" "nextcloud-vm" {
                name        = local.vm_name
                platform_id = "standard-v2"
                zone        = "ru-central1-a"
              
                resources {
                  cores  = "2"
                  memory = "4"
                }
              
                boot_disk {
                  disk_id = yandex_compute_disk.boot-disk.id
                }
              
                network_interface {
                  subnet_id          = yandex_vpc_subnet.nextcloud-subnet-a.id
                  nat                = true
                  security_group_ids = [yandex_vpc_security_group.nextcloud-sg.id]
                }
              
                metadata = {
                  user-data = "#cloud-config\nusers:\n  - name: yc-user\n    groups: sudo\n    shell: /bin/bash\n    sudo: 'ALL=(ALL) NOPASSWD:ALL'\n    ssh-authorized-keys:\n      - ${file(var.ssh_key_path)}"
                }
              }
              
              # Creating a MySQL cluster
              
              resource "yandex_mdb_mysql_cluster" "nextcloud-cluster" {
                name        = local.cluster_name
                environment = "PRODUCTION"
                network_id  = yandex_vpc_network.nextcloud-network.id
                version     = "8.0"
                security_group_ids = [yandex_vpc_security_group.nextcloud-sg.id]
              
                resources {
                  resource_preset_id = "s2.micro"
                  disk_type_id       = "network-ssd"
                  disk_size          = 10
                }
              
                host {
                  zone      = "ru-central1-a"
                  subnet_id = yandex_vpc_subnet.nextcloud-subnet-a.id
                }
              
                mysql_config = {
                  sql_mode             = "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"
                  character_set_server = "utf8mb4"
                  collation_server     = "utf8mb4_general_ci"
                }
              }
              
              # Creating a database in the MySQL cluster
              
              resource "yandex_mdb_mysql_database" "nextcloud-db" {
                cluster_id = yandex_mdb_mysql_cluster.nextcloud-cluster.id
                name       = local.db_name
              }
              
              # Creating a database user in the MySQL cluster
              
              resource "yandex_mdb_mysql_user" "my_user" {
                cluster_id = yandex_mdb_mysql_cluster.nextcloud-cluster.id
                name       = local.db_username
                password   = var.db_password
              
                permission {
                  database_name = yandex_mdb_mysql_database.nextcloud-db.name
                  roles         = ["ALL"]
                }
              }
              
              # Displaying static access key data on the screen
              
              output "Access_key" {
                value = yandex_iam_service_account_static_access_key.sa-static-key.access_key
              }
              
              output "Secret_key" {
                value = yandex_iam_service_account_static_access_key.sa-static-key.secret_key
                sensitive = true
              }
              ```

              {% endcut %}

          1. `nextcloud-integrate-storage.auto.tfvars` user data file:

              {% cut "nextcloud-integrate-storage.auto.tfvars" %}

              ```hcl
              folder_id     = "<folder_ID>"
              ssh_key_path  = "<path_to_public_SSH_key>"
              bucket_name   = "<bucket_name>"
              db_password   = "<database_user_password>"
              domain_name   = "<domain_name>"
              ```

              {% endcut %}

    {% endlist %}

    Learn more about the properties of Terraform resources in the relevant provider guides:
    * [Network](../../../vpc/concepts/network.md#network): [yandex_vpc_network](../../../terraform/resources/vpc_network.md).
    * [NAT gateway](../../../vpc/concepts/gateways.md#nat-gateway): [yandex_vpc_gateway](../../../terraform/resources/vpc_gateway.md).
    * [Route table](../../../vpc/concepts/routing.md#rt-vpc): [yandex_vpc_route_table](../../../terraform/resources/vpc_route_table.md).
    * [Subnet](../../../vpc/concepts/network.md#subnet): [yandex_vpc_subnet](../../../terraform/resources/vpc_subnet.md).
    * [Security group](../../../vpc/concepts/security-groups.md): [yandex_vpc_security_group](../../../terraform/resources/vpc_security_group.md).
    * [Service account](../../../iam/concepts/users/service-accounts.md): [yandex_iam_service_account](../../../terraform/resources/iam_service_account.md).
    * [Role](../../../iam/concepts/access-control/roles.md): [yandex_resourcemanager_folder_iam_member](../../../terraform/resources/resourcemanager_folder_iam_member.md)
    * [Static access key](../../../iam/concepts/authorization/access-key.md): [yandex_iam_service_account_static_access_key](../../../terraform/resources/iam_service_account_static_access_key.md).
    * [Bucket](../../../storage/concepts/bucket.md): [yandex_storage_bucket](../../../terraform/resources/storage_bucket.md).
    * VM [disk](../../concepts/disk.md): [yandex_compute_disk](../../../terraform/resources/compute_disk.md).
    * [VM](../../concepts/vm.md) instance: [yandex_compute_instance](../../../terraform/resources/compute_instance.md).
    * [Managed Service for MySQL®](../../../managed-mysql/concepts/index.md) cluster: [yandex_mdb_mysql_cluster](../../../terraform/resources/mdb_mysql_cluster.md).
    * MySQL® database: [yandex_mdb_mysql_database](../../../terraform/resources/mdb_mysql_database.md).
    * MySQL® database user: [yandex_mdb_mysql_user](../../../terraform/resources/mdb_mysql_user.md).

1. In the `nextcloud-integrate-storage.auto.tfvars` file, set the values of the user-defined variables:
    * `folder_id`: [Folder ID](../../../resource-manager/operations/folder/get-id.md).
    * `ssh_key_path`: Path to the public SSH key file. For more information, see [Creating an SSH key pair](../../operations/vm-connect/ssh.md#creating-ssh-keys).
    * `bucket_name`: Bucket name consistent with the [naming conventions](../../../storage/concepts/bucket.md#naming).
    * `db_password`: MySQL® database user password.
    * `domain_name`: Name of the domain to host the Nextcloud instance.

        To use domain names in the public DNS zone, you need to delegate it to authoritative name servers. Specify `ns1.yandexcloud.net` and `ns2.yandexcloud.net` server addresses in your registrar's account settings.

        {% note info %}

        If you only plan to create a basic Nextcloud instance without deploying a fault-tolerant solution, you do not have to change the value of the `domain_name` variable.

        {% endnote %}

1. Create the resources:

   1. In the terminal, navigate to the configuration file directory.
   1. Make sure the configuration is correct using this command:
   
      ```bash
      terraform validate
      ```
   
      If the configuration is valid, you will get this message:
   
      ```bash
      Success! The configuration is valid.
      ```
   
   1. Run this command:
   
      ```bash
      terraform plan
      ```
   
      You will see a list of resources and their properties. No changes will be made at this step. Terraform will show any errors in the configuration.
   1. Apply the configuration changes:
   
      ```bash
      terraform apply
      ```
   
   1. Type `yes` and press **Enter** to confirm the changes.

This will create all the infrastructure you need to deploy Nextcloud in a basic configuration in the selected folder.

Terraform will display the ID of the created [static access key](../../../iam/concepts/authorization/access-key.md). You will need this value later when setting up Nextcloud integration with Object Storage. You will also need a secret key. To get it, run the following command in the terminal:

```bash
terraform output Secret_key
```

Once you have the secret key value, proceed to configuring Nextcloud on your VM.

### Install and configure Nextcloud on the VM {#setup-nextcloud}

1. Install Nextcloud on `nextcloud-vm`:

    1. [Connect](../../operations/vm-connect/ssh.md#vm-connect) to `nextcloud-vm` over SSH.

        To do this, run this command in the VM terminal, specifying the public IP address of the VM:

        ```bash
        ssh yc-user@<VM_IP_address>
        ```

        You can look up the VM's public IP address in the [management console](https://console.yandex.cloud) by checking the **Network** section's **Public IPv4 address** field on the VM information page.
    1. Upgrade the versions of the packages installed on the VM:
       
       ```bash
       sudo apt update && sudo apt upgrade
       ```
    1. Install the required software packages and dependencies:
       
       ```bash
       sudo apt install \
         apache2 mariadb-server libapache2-mod-php php-gd php-mysql php-curl php-mbstring \
         php-intl php-gmp php-bcmath php-xml php-imagick php-zip php-fpm unzip
       ```
    1. Download the archive with the latest Nextcloud version:
       
       ```bash
       wget https://download.nextcloud.com/server/releases/latest.zip
       ```
    1. Unpack the archive to the `/var/www` directory:
       
       ```bash
       sudo unzip latest.zip -d /var/www
       ```
    1. Edit access permissions for the Nextcloud directory:
       
       ```bash
       sudo chown -R www-data:www-data /var/www/nextcloud
       sudo chmod -R 755 /var/www/nextcloud/
       ```
    1. Configure the default virtual host:
       
       1. Open the configuration file of the default virtual host:
       
           ```bash
           sudo nano /etc/apache2/sites-available/000-default.conf
           ```
       1. Replace the contents of `000-default.conf` with the following:
       
           ```text
           <VirtualHost *:80>
           DocumentRoot /var/www/nextcloud/
       
           <Directory /var/www/nextcloud/>
           Require all granted
           AllowOverride All
           Options FollowSymLinks MultiViews
       
           <IfModule mod_dav.c>
           Dav off
           </IfModule>
           </Directory>
           </VirtualHost>
           ```
    1. Enable the required [Apache web server](https://en.wikipedia.org/wiki/Apache_HTTP_Server) modules:
       
       ```bash
       sudo a2enmod rewrite
       sudo a2enmod headers
       a2enmod env
       a2enmod dir
       a2enmod mime
       ```
    1. Increase the amount of RAM available to the PHP interpreter for processing requests to Nextcloud to `512 MB`.
       
       1. Open the `.htaccess` file in the Nextcloud installation directory:
       
           ```bash
           sudo nano /var/www/nextcloud/.htaccess
           ```
       1. Add the following line to the end of the file:
       
           ```bash
           php_value memory_limit 512M
           ```
       
           Make sure to save your changes.
    1. Restart the web server:
       
       ```bash
       sudo systemctl restart apache2
       ```
1. Configure Nextcloud in the GUI:

    1. Open your local computer browser and enter the following in the address bar:
       
       ```text
       http://<VM_public_IP_address>
       ```
       
       You can look up the VM's public IP address in the [management console](https://console.yandex.cloud) by checking the **Network** section's **Public IPv4 address** field on the VM information page.
    1. In the **Create an admin account** form that opens:

        1. In the **New admin account name** and **New admin password** fields, set the Nextcloud administrator credentials you will use to log in to the system.
        1. In the **Database account** field, specify `user`, which is the name of the database user created in the MySQL® cluster.
        1. In the **Database password** field, enter the database user password you specified in the `nextcloud-integrate-storage.auto.tfvars` file.
        1. In the **Database name** field, specify `nextcloud`, which is the name of the database created in the MySQL® cluster.
        1. In the **Database host** field, specify the FQDN of the MySQL® cluster’s [current master host](../../../managed-mysql/operations/connect/fqdn.md#fqdn-master) and port in this format:

            ```text
            c-<cluster_ID>.rw.mdb.yandexcloud.net:3306
            ```

            You can look up the cluster ID in the [management console](https://console.yandex.cloud) by checking the **ID** field on the cluster information page.
        1. Click **Install**.

            This will start the deployment of the Nextcloud database in the MySQL® cluster. Wait for this process to complete.
    1. After the installation is over, you will see a window with recommended apps. Click **Skip**. You can get back to installing the applications you need at a later time.
    1. Close the window with information on updates in the current Nextcloud version.
    1. Open the application management menu. To do this, click the user icon in the top-right corner of the screen and select ![plus](../../../_assets/console-icons/plus.svg) **Apps** from the context menu.
    1. In the window that opens, select ![person](../../../_assets/console-icons/person.svg) **Your apps** in the left-hand panel.
    1. In the list that opens, find the `External storage support` application and click **Enable** in the row with it.
       
       If needed, enter your Nextcloud administrator password in the pop-up window to confirm the operation.
    1. Open the main settings menu. To do this, click the user icon in the top-right corner of the screen and select ![admin-icon](../../../_assets/tutorials/integrate-nextcloud/admin-icon.svg) **Administration settings** from the context menu.
    1. In the window that opens, select ![app-dark-icon](../../../_assets/tutorials/integrate-nextcloud/app-dark-icon.svg) **External storage** in the left-hand panel under **Administration** and specify the Object Storage integration settings under **External storage** in the window that opens:
       
       1. In the **External storage** section, select `Amazon S3`.
       1. In the **Authentication** section, select `Access key`.
       1. In the **Configuration** section:
       
           * In the **Bucket** field, enter the name of the bucket you created earlier, e.g., `my-nextcloud-bucket`.
           * In the **Hostname** field, specify `storage.yandexcloud.net`.
           * In the **Port** field, specify `443`.
           * In the **Access key** field, paste the static access key ID you got earlier.
           * In the **Secret key** field, paste the static access key's secret key you got earlier.
       1. Under **Available for**, enable **All people**.
       1. On the right side of the section you are editing, click the ![check](../../../_assets/console-icons/check.svg) icon to save your changes.
       
           Enter your Nextcloud administrator password in the pop-up window to confirm the operation.

### Test the solution in the basic configuration {#test-simple}

To test Yandex Object Storage integration with Nextcloud on a single host:

1. Open your local computer browser and enter the public IPv4 address of the Nextcloud VM in the address bar:

    ```text
    http://<VM_public_IP_address>
    ```
1. Get authenticated in Nextcloud using the login and password created when configuring the solution in the previous step.
1. In the left pane of the top menu, select ![folder-fill](../../../_assets/console-icons/folder-fill.svg) **Files**.
1. In the left-hand menu, select **External storage** and then **AmazonS3**.
1. Click ![plus](../../../_assets/console-icons/plus.svg) **New** and select ![arrow-shape-up-from-line](../../../_assets/console-icons/arrow-shape-up-from-line.svg) **Upload files** to upload a file from your local computer to the storage.
1. Select a file on your local computer and upload it to the storage.

    The uploaded file will now appear in the Nextcloud storage named `AmazonS3`.
1. In Yandex Object Storage, [make sure](../../../storage/operations/objects/list.md) the file was uploaded to the bucket.

The deployment of the Nextcloud basic configuration is now complete. You can now proceed to deploy the fault-tolerant configuration.

## Deploy Nextcloud in a fault-tolerant configuration {#the-redundant-variant}

You will deploy a fault-tolerant Nextcloud configuration in a group of three VMs, the load on Nextcloud hosts distributed with the help of an L7 Yandex Application Load Balancer. The service database will reside in a three-host MySQL® cluster. Hosts of the instance group, load balancer, and MySQL® cluster will be evenly distributed across three [availability zones](../../../overview/concepts/geo-scope.md). Nextcloud will be available via the domain name, for which a TLS certificate will be issued in Certificate Manager.

### Complete Nextcloud setup {#tune-nextcloud}

Before you begin deploying a fault-tolerant configuration, add your domain to Nextcloud's list of trusted addresses and domains:

1. [Connect](../../operations/vm-connect/ssh.md#vm-connect) to `nextcloud-vm` over SSH.
1. Add your domain to the array of trusted addresses and Nextcloud domains:
   
   1. In the VM terminal, open the Nextcloud configuration file:
   
       ```bash
       sudo nano /var/www/nextcloud/config/config.php
       ```
   1. In the `trusted_domains` array, replace the host IP address with your domain name.
   
       Here is an example:
   
       ```php
       'trusted_domains' =>
       array (
         0 => 'example.com',
       ),
       ```
   
       The `trusted_domains` array allows you to restrict the range of IP addresses and/or domains you can use to access Nextcloud and ensures additional protection from unauthorized access. You can specify multiple addresses and/or domains, and you can also reduce or remove this restriction using wildcard characters:
   
       {% list tabs %}
   
       - Example 1
   
         ```php
         'trusted_domains' =>
         array (
           0 => '*.example.com',
           1 => '198.168.*.*',
         ),
         ```
   
         In this example, access is allowed from any subdomains of the `example.com` domain and the IP addresses of the `192.168.0.0` - `192.168.255.255` range.
   
       - Example 2
   
         ```php
         'trusted_domains' =>
         array (
           0 => '*',
         ),
         ```
   
         In this example, access is allowed from any domains and IP addresses.
   
       {% endlist %}
   1. Delete the `'overwrite.cli.url' => 'http://<VM_IP_address>',` line.
   1. Save the changes and close the `nano` editor.
1. [Stop](../../operations/vm-control/vm-stop-and-start.md#stop) the `nextcloud-vm` virtual machine.

### Scale the infrastructure for the fault-tolerant configuration {#create-failsafe-infrastructure}

1. Set up your infrastructure description files:

    {% list tabs group=infrastructure_description %}

    - Ready-made configuration {#ready}

      1. In the folder with the downloaded repository, delete the `nextcloud-integrate-storage-basic-config.tf` file.
      1. From the `failsafe` folder, move the `nextcloud-integrate-storage-failsafe-config.tf` file to the repository’s root folder, i.e., the one you just deleted the file from.

    - Manually {#manual}

      1. In the previously created folder for configuration files, delete the `nextcloud-integrate-storage-basic-config.tf` file.
      1. Create a new `nextcloud-integrate-storage-failsafe-config.tf` configuration file in this folder:

          {% cut "nextcloud-integrate-storage-failsafe-config.tf" %}

          ```hcl
          # Declaring variables
          
          variable "folder_id" {
            type = string
          }
          
          variable "bucket_name" {
            type = string
          }
          
          variable "ssh_key_path" {
            type = string
          }
          
          variable "db_password" {
            type = string
            sensitive  = true
          }
          
          variable "domain_name" {
            type = string
          }
          
          locals {
            sa_name             = "nextcloud-sa"
            network_name        = "nextcloud-network"
            subnet_a_name       = "nextcloud-subnet-ru-central1-a"
            subnet_b_name       = "nextcloud-subnet-ru-central1-b"
            subnet_d_name       = "nextcloud-subnet-ru-central1-d"
            subnet-a-cidr       = "192.168.11.0/24"
            subnet-b-cidr       = "192.168.12.0/24"
            subnet-d-cidr       = "192.168.13.0/24"
            sg_name             = "nextcloud-sg"
            gw_name             = "nextcloud-gateway"
            vm_name             = "nextcloud-vm"
            vm_image_id         = "fd8vsghfu10ev5gdatkh"
            ig_name             = "nextcloud-instance-group"
            cluster_name        = "nextcloud-db-cluster"
            db_name             = "nextcloud"
            db_username         = "user"
            domain_zone_name    = "nexcloud-domain-zone"
            certificate_name    = "mymanagedcert"
            snapshot_name       = "nextcloud-vm-snapshot"
            tg_name             = "nextcloud-target-group"
            backend_name        = "nextcloud-backend"
            bg_name             = "nextcloud-bg"
            http_router_name    = "nextcloud-router"
            vh_name             = "nextcloud-vh"
            route_name          = "nextcloud-route"
            alb_name            = "nextcloud-alb"
            listener_name       = "nextcloud-listener"
          }
          
          # Configuring the provider
          
          terraform {
            required_providers {
              yandex = {
                source = "yandex-cloud/yandex"
              }
            }
            required_version = ">= 0.13"
          }
          
          provider "yandex" {
            folder_id = var.folder_id
          }
          
          # Create network
          
          resource "yandex_vpc_network" "nextcloud-network" {
            name = local.network_name
          }
          
          # Creating a NAT gateway
          
          resource "yandex_vpc_gateway" "nextcloud-gw" {
            name = local.gw_name
            shared_egress_gateway {}
          }
          
          # Creating a routing table
          
          resource "yandex_vpc_route_table" "nextcloud-rt-table" {
            network_id  = yandex_vpc_network.nextcloud-network.id
          
            static_route {
              destination_prefix = "0.0.0.0/0"
              gateway_id         = yandex_vpc_gateway.nextcloud-gw.id
            }
          }
          
          # Creating subnets
          
          resource "yandex_vpc_subnet" "nextcloud-subnet-a" {
            name           = local.subnet_a_name
            zone           = "ru-central1-a"
            network_id     = yandex_vpc_network.nextcloud-network.id
            v4_cidr_blocks = [local.subnet-a-cidr]
            route_table_id = yandex_vpc_route_table.nextcloud-rt-table.id
          }
          
          resource "yandex_vpc_subnet" "nextcloud-subnet-b" {
            name           = local.subnet_b_name
            zone           = "ru-central1-b"
            network_id     = yandex_vpc_network.nextcloud-network.id
            v4_cidr_blocks = [local.subnet-b-cidr]
            route_table_id = yandex_vpc_route_table.nextcloud-rt-table.id
          }
          
          resource "yandex_vpc_subnet" "nextcloud-subnet-d" {
            name           = local.subnet_d_name
            zone           = "ru-central1-d"
            network_id     = yandex_vpc_network.nextcloud-network.id
            v4_cidr_blocks = [local.subnet-d-cidr]
            route_table_id = yandex_vpc_route_table.nextcloud-rt-table.id
          }
          
          # Creating a security group
          
          resource "yandex_vpc_security_group" "nextcloud-sg" {
            name        = local.sg_name
            network_id  = yandex_vpc_network.nextcloud-network.id
          
            ingress {
              protocol       = "TCP"
              description    = "http"
              v4_cidr_blocks = ["0.0.0.0/0"]
              port           = 80
            }
          
            ingress {
              protocol       = "TCP"
              description    = "https"
              v4_cidr_blocks = ["0.0.0.0/0"]
              port           = 443
            }
          
            ingress {
              protocol       = "TCP"
              description    = "ssh"
              v4_cidr_blocks = ["0.0.0.0/0"]
              port           = 22
            }
          
            ingress {
              protocol       = "ANY"
              description    = "self"
              predefined_target = "self_security_group"
              from_port      = 0
              to_port        = 65535
            }
          
            ingress {
              protocol       = "ANY"
              description    = "healthchecks"
              predefined_target = "loadbalancer_healthchecks"
              from_port      = 0
              to_port        = 65535
            }
          
            egress {
              protocol       = "ANY"
              description    = "any"
              v4_cidr_blocks = ["0.0.0.0/0"]
              from_port      = 0
              to_port        = 65535
            }
          }
          
          # Creating a service account
          
          resource "yandex_iam_service_account" "nextcloud-sa" {
            name = local.sa_name
          }
          
          # Assigning roles to a service account
          
          resource "yandex_resourcemanager_folder_iam_member" "editor" {
            folder_id = var.folder_id
            role      = "editor"
            member    = "serviceAccount:${yandex_iam_service_account.nextcloud-sa.id}"
          }
          
          # Creating a static access key for CA
          
          resource "yandex_iam_service_account_static_access_key" "sa-static-key" {
            service_account_id = yandex_iam_service_account.nextcloud-sa.id
          }
          
          # Creating a bucket
          
          resource "yandex_storage_bucket" "nextcloud-bucket" {
            access_key = yandex_iam_service_account_static_access_key.sa-static-key.access_key
            secret_key = yandex_iam_service_account_static_access_key.sa-static-key.secret_key
            bucket     = var.bucket_name
          }
          
          # Creating a boot disk for the VM
          
          resource "yandex_compute_disk" "boot-disk" {
            type     = "network-ssd"
            zone     = "ru-central1-a"
            size     = "24"
            image_id = local.vm_image_id
          }
          
          # Creating a VM
          
          resource "yandex_compute_instance" "nextcloud-vm" {
            name        = local.vm_name
            platform_id = "standard-v2"
            zone        = "ru-central1-a"
          
            resources {
              cores  = "2"
              memory = "4"
            }
          
            boot_disk {
              disk_id = yandex_compute_disk.boot-disk.id
            }
          
            network_interface {
              subnet_id          = yandex_vpc_subnet.nextcloud-subnet-a.id
              nat                = true
              security_group_ids = [yandex_vpc_security_group.nextcloud-sg.id]
            }
          
            metadata = {
              user-data = "#cloud-config\nusers:\n  - name: yc-user\n    groups: sudo\n    shell: /bin/bash\n    sudo: 'ALL=(ALL) NOPASSWD:ALL'\n    ssh-authorized-keys:\n      - ${file(var.ssh_key_path)}"
            }
          }
          
          # Creating a MySQL cluster
          
          resource "yandex_mdb_mysql_cluster" "nextcloud-cluster" {
            name        = local.cluster_name
            environment = "PRODUCTION"
            network_id  = yandex_vpc_network.nextcloud-network.id
            version     = "8.0"
            security_group_ids = [yandex_vpc_security_group.nextcloud-sg.id]
          
            resources {
              resource_preset_id = "s2.micro"
              disk_type_id       = "network-ssd"
              disk_size          = 10
            }
          
            host {
              zone      = "ru-central1-a"
              subnet_id = yandex_vpc_subnet.nextcloud-subnet-a.id
            }
          
            host {
              zone      = "ru-central1-b"
              subnet_id = yandex_vpc_subnet.nextcloud-subnet-b.id
            }
          
            host {
              zone      = "ru-central1-d"
              subnet_id = yandex_vpc_subnet.nextcloud-subnet-d.id
            }
          
            mysql_config = {
              sql_mode             = "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"
              character_set_server = "utf8mb4"
              collation_server     = "utf8mb4_general_ci"
            }
          }
          
          # Creating a database in a MySQL cluster
          
          resource "yandex_mdb_mysql_database" "nextcloud-db" {
            cluster_id = yandex_mdb_mysql_cluster.nextcloud-cluster.id
            name       = local.db_name
          }
          
          # Creating a database user in a MySQL cluster
          
          resource "yandex_mdb_mysql_user" "my_user" {
            cluster_id = yandex_mdb_mysql_cluster.nextcloud-cluster.id
            name       = local.db_username
            password   = var.db_password
          
            permission {
              database_name = yandex_mdb_mysql_database.nextcloud-db.name
              roles         = ["ALL"]
            }
          }
          
          # Creating a disk snapshot
          
          resource "yandex_compute_snapshot" "nextcloud-snapshot" {
            name           = local.snapshot_name
            source_disk_id = yandex_compute_disk.boot-disk.id
          }
          
          # Creating a public DNS zone
          
          resource "yandex_dns_zone" "nextcloud-domain-zone" {
            name    = local.domain_zone_name
            zone    = "${var.domain_name}."
            public  = true
          }
          
          # Adding a Let's Encrypt certificate
          
          resource "yandex_cm_certificate" "nextcloud-certificate" {
            name    = local.certificate_name
            domains = [var.domain_name]
          
            managed {
            challenge_type = "DNS_CNAME"
            challenge_count = 1
            }
          }
          
          # Creating CNAME records for domain validation when issuing a certificate
          
          resource "yandex_dns_recordset" "validation-record" {
            count   = yandex_cm_certificate.nextcloud-certificate.managed[0].challenge_count
            zone_id = yandex_dns_zone.nextcloud-domain-zone.id
            name    = yandex_cm_certificate.nextcloud-certificate.challenges[count.index].dns_name
            type    = yandex_cm_certificate.nextcloud-certificate.challenges[count.index].dns_type
            ttl     = 600
            data    = [yandex_cm_certificate.nextcloud-certificate.challenges[count.index].dns_value]
          }
          
          # Waiting for domain validation and the issue of a Let's Encrypt certificate
          
          data "yandex_cm_certificate" "wait-nextcloud-certificate" {
            depends_on      = [yandex_dns_recordset.validation-record]
            certificate_id  = yandex_cm_certificate.nextcloud-certificate.id
            wait_validation = true
          }
          
          # Creating an instance group
          
          resource "yandex_compute_instance_group" "nextcloud-ig" {
            name                = local.ig_name
            service_account_id  = yandex_iam_service_account.nextcloud-sa.id
            instance_template {
              platform_id = "standard-v2"
              resources {
                memory = 4
                cores  = 2
              }
              boot_disk {
                mode = "READ_WRITE"
                initialize_params {
                  snapshot_id = yandex_compute_snapshot.nextcloud-snapshot.id
                  size     = 24
                }
              }
          
              network_interface {
                subnet_ids         = [yandex_vpc_subnet.nextcloud-subnet-a.id,yandex_vpc_subnet.nextcloud-subnet-b.id,yandex_vpc_subnet.nextcloud-subnet-d.id]
                security_group_ids = [yandex_vpc_security_group.nextcloud-sg.id]
              }
          
              metadata = {
                ssh-keys = "yc-user:${file(var.ssh_key_path)}"
              }
            }
          
            scale_policy {
              fixed_scale {
                size = 3
              }
            }
          
            allocation_policy {
              zones = ["ru-central1-a","ru-central1-b","ru-central1-d"]
            }
          
            deploy_policy {
              max_unavailable = 2
              max_expansion   = 1
            }
          
            application_load_balancer {
              target_group_name = local.tg_name
            }
          
            depends_on = [
              yandex_resourcemanager_folder_iam_member.editor
            ]
          }
          
          # Creating a backend group
          
          resource "yandex_alb_backend_group" "nextcloud-backend-group" {
            name = local.bg_name
          
            session_affinity {
              connection {
                source_ip = true
              }
            }
          
            http_backend {
              name             = local.backend_name
              weight           = 1
              port             = 80
              target_group_ids = [yandex_compute_instance_group.nextcloud-ig.application_load_balancer[0].target_group_id]
              load_balancing_config {
                mode = "MAGLEV_HASH"
              }
            }
          }
          
          # Creating an HTTP router
          
          resource "yandex_alb_http_router" "nextcloud-router" {
            name = local.http_router_name
          }
          
          # Creating a virtual host
          
          resource "yandex_alb_virtual_host" "nextcloud-vhost" {
            name           = local.vh_name
            http_router_id = yandex_alb_http_router.nextcloud-router.id
            route {
              name = local.route_name
              http_route {
                http_route_action {
                  backend_group_id = yandex_alb_backend_group.nextcloud-backend-group.id
                  timeout          = "60s"
                }
              }
            }
          }
          
          # Creating an L7 load balancer
          
          resource "yandex_alb_load_balancer" "nextcloud-alb" {
            name = local.alb_name
          
            network_id = yandex_vpc_network.nextcloud-network.id
          
            allocation_policy {
              location {
                zone_id   = "ru-central1-a"
                subnet_id = yandex_vpc_subnet.nextcloud-subnet-a.id
              }
              location {
                zone_id   = "ru-central1-b"
                subnet_id = yandex_vpc_subnet.nextcloud-subnet-b.id
              }
              location {
                zone_id   = "ru-central1-d"
                subnet_id = yandex_vpc_subnet.nextcloud-subnet-d.id
              }
            }
          
            security_group_ids = [yandex_vpc_security_group.nextcloud-sg.id]
          
            listener {
              name = local.listener_name
              endpoint {
                address {
                  external_ipv4_address {
                  }
                }
                ports = [443]
              }
          
              tls {
                default_handler {
                  certificate_ids = [data.yandex_cm_certificate.wait-nextcloud-certificate.id]
                  http_handler {
                    http_router_id = yandex_alb_http_router.nextcloud-router.id
                  }
                }
              }
            }
          }
          
          # Creating a type A resource record for the L7 load balancer
          
          resource "yandex_dns_recordset" "nextcloud-a-record" {
            zone_id = yandex_dns_zone.nextcloud-domain-zone.id
            name    = yandex_dns_zone.nextcloud-domain-zone.zone
            type    = "A"
            ttl     = 600
            data    = [yandex_alb_load_balancer.nextcloud-alb.listener[0].endpoint[0].address[0].external_ipv4_address[0].address]
          }
          ```

          {% endcut %}

    {% endlist %}

    For more information about the properties of resources added to Terraform, refer to the relevant provider guides:
    * [Disk snapshot](../../concepts/snapshot.md): [yandex_compute_snapshot](../../../terraform/resources/compute_snapshot.md)
    * [DNS zone](../../../dns/concepts/dns-zone.md): [yandex_dns_zone](../../../terraform/resources/dns_zone.md)
    * [TLS certificate](../../../certificate-manager/concepts/managed-certificate.md): [yandex_cm_certificate](../../../terraform/resources/cm_certificate.md)
    * [DNS resource record](../../../dns/concepts/resource-record.md): [yandex_dns_recordset](../../../terraform/resources/dns_recordset.md)
    * [Instance group](../../concepts/instance-groups/index.md): [yandex_compute_instance_group](../../../terraform/resources/compute_instance_group.md)
    * [Backend group](../../../application-load-balancer/concepts/backend-group.md): [yandex_alb_backend_group](../../../terraform/resources/alb_backend_group.md)
    * [HTTP router](../../../application-load-balancer/concepts/http-router.md): [yandex_alb_http_router](../../../terraform/resources/alb_http_router.md)
    * [Virtual host](../../../application-load-balancer/concepts/http-router.md#virtual-host): [yandex_alb_virtual_host](../../../terraform/resources/alb_virtual_host.md)
    * [L7 load balancer](../../../application-load-balancer/concepts/application-load-balancer.md): [yandex_alb_load_balancer](../../../terraform/resources/alb_load_balancer.md)

1. Create the resources:

   1. In the terminal, navigate to the configuration file directory.
   1. Make sure the configuration is correct using this command:
   
      ```bash
      terraform validate
      ```
   
      If the configuration is valid, you will get this message:
   
      ```bash
      Success! The configuration is valid.
      ```
   
   1. Run this command:
   
      ```bash
      terraform plan
      ```
   
      You will see a list of resources and their properties. No changes will be made at this step. Terraform will show any errors in the configuration.
   1. Apply the configuration changes:
   
      ```bash
      terraform apply
      ```
   
   1. Type `yes` and press **Enter** to confirm the changes.

As a result, a fault-tolerant Nextcloud solution will be deployed in the selected folder.

### Test the solution in the fault-tolerant configuration {#test-redundant}

To test Yandex Object Storage integration with Nextcloud in a fault-tolerant configuration:

1. Open your local computer browser and enter your domain name in the address bar, e.g.:

    ```text
    https://example.com
    ```
1. Get authenticated in Nextcloud using the login and password created when configuring the solution.
1. In the left pane of the top menu, select ![folder-fill](../../../_assets/console-icons/folder-fill.svg) **Files**.
1. In the left-hand menu, select **External storage** and then **AmazonS3**.
1. Make sure you see the file uploaded in the previous step.
1. Download the file you uploaded earlier. Do this by clicking ![ellipsis](../../../_assets/console-icons/ellipsis.svg) and selecting ![arrow-down](../../../_assets/console-icons/arrow-down.svg) **Download** in the line with the filename.
1. Delete the file. Do this by clicking ![ellipsis](../../../_assets/console-icons/ellipsis.svg) and selecting ![trash-bin](../../../_assets/console-icons/trash-bin.svg) **Delete file** in the line with the filename.
1. In Yandex Object Storage, [make sure](../../../storage/operations/objects/list.md) the file was deleted from the bucket.

## How to delete the resources you created {#clear-out}

{% note warning %}

Before deleting the infrastructure, [delete](../../../storage/operations/objects/delete.md) all objects in the created bucket.

{% endnote %}

To stop paying for the resources you created:

1. Open the `nextcloud-integrate-storage-failsafe-config.tf` file and delete your infrastructure description from it.
1. Apply the changes:

    1. In the terminal, navigate to the configuration file directory.
    1. Make sure the configuration is correct using this command:
    
       ```bash
       terraform validate
       ```
    
       If the configuration is valid, you will get this message:
    
       ```bash
       Success! The configuration is valid.
       ```
    
    1. Run this command:
    
       ```bash
       terraform plan
       ```
    
       You will see a list of resources and their properties. No changes will be made at this step. Terraform will show any errors in the configuration.
    1. Apply the configuration changes:
    
       ```bash
       terraform apply
       ```
    
    1. Type `yes` and press **Enter** to confirm the changes.

#### See also {#see-also}

* [Deploying Nextcloud on a Compute Cloud VM from a Container Optimized Image image, integrated with Yandex Object Storage](coi-based.md)
* [Manually deploying Nextcloud on a Compute Cloud VM or in a VM group, integrated with Yandex Object Storage](fault-tolerant.md)