Files
twenty/packages/twenty-docs/docs/start/self-hosting/cloud-providers.mdx
Charles Bochet e576fe0d67 Update yarn commands (#4644)
* Simplify commands

* Simplify commands

* Migrate all dev commands to project.json

* Fix tests
2024-03-25 12:31:18 +01:00

427 lines
11 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Cloud providers
sidebar_position: 2
sidebar_custom_props:
icon: TbCloud
---
:::info
This document is maintained by the community. It might contain issues.
Feel free to join our discord if you need assistance.
:::
## Available cloud providers
- [Azure Container Apps](#azure-container-apps)
- [Others](#others)
## Azure Container Apps
### About
Hosts Twenty CRM using Azure Container Apps.
The solution provisions file shares, a container apps environment with three containers, and a log analytics workspace.
The file shares are used to store uploaded images and files through the UI, and to store database backups.
### Prerequisites
- Terraform installed https://developer.hashicorp.com/terraform/install
- An Azure subscription with permissions to create resources
### Step by step instructions:
1. Create a new folder and copy all the files from below
2. Run `terraform init`
3. Run `terraform plan -out tfplan`
4. Run `terraform apply tfplan`
5. Connect to server `az containerapp exec --name twenty-server -g twenty-crm-rg`
6. Initialize the database from the server `yarn database:init:prod`
7. Go to https://your-twenty-front-fqdn - located in the portal
#### Production docker containers
This uses the prebuilt images found on [docker hub](https://hub.docker.com/r/twentycrm/).
#### Environment Variables
- Is set in respective tf-files
- See docs [Setup Environment Variables](https://docs.twenty.com/start/self-hosting/) for usage
- After deployment you could can set `IS_SIGN_UP_DISABLED=true` (and run `terraform plan/apply` again) to disable new workspaces from being created
#### Security and networking
- Container `twenty-db` accepts only ingress TCP traffic from other containers in the environment. No external ingress traffic allowed
- Container `twenty-server` accepts external traffic over HTTPS
- Container `twenty-front` accepts external traffic over HTTPS
It´s highly recommended to enable [built-in authentication](https://learn.microsoft.com/en-us/azure/container-apps/authentication) for `twenty-front` using one of the supported providers.
Use the [custom domain](https://learn.microsoft.com/en-us/azure/container-apps/custom-domains-certificates) feature on the `twenty-front` container if you would like an easier domain name.
#### Files
##### providers.tf
```hcl
# providers.tf
terraform {
required_providers {
azapi = {
source = "Azure/azapi"
}
}
}
provider "azapi" {
}
provider "azurerm" {
features {}
}
provider "azuread" {
}
provider "random" {
}
```
##### main.tf
```hcl
# main.tf
# Create a resource group
resource "azurerm_resource_group" "main" {
name = "twenty-crm-rg"
location = "North Europe"
}
# Variables
locals {
app_env_name = "twenty"
server_name = "twenty-server"
server_tag = "latest"
front_app_name = "twenty-front"
front_tag = "latest"
db_app_name = "twenty-postgres"
db_tag = "latest"
db_user = "twenty"
db_password = "twenty"
storage_mount_db_name = "twentydbstoragemount"
storage_mount_server_name = "twentyserverstoragemount"
cpu = 1.0
memory = "2Gi"
}
# Set up a Log Analytics workspace
resource "azurerm_log_analytics_workspace" "main" {
name = "${local.app_env_name}-law"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
sku = "PerGB2018"
retention_in_days = 30
}
# Create a storage account
resource "random_pet" "example" {
length = 2
separator = ""
}
resource "azurerm_storage_account" "main" {
name = "twentystorage${random_pet.example.id}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
account_tier = "Standard"
account_replication_type = "LRS"
large_file_share_enabled = true
}
# Create db file storage
resource "azurerm_storage_share" "db" {
name = "twentydatabaseshare"
storage_account_name = azurerm_storage_account.main.name
quota = 50
enabled_protocol = "SMB"
}
# Create backend file storage
resource "azurerm_storage_share" "server" {
name = "twentyservershare"
storage_account_name = azurerm_storage_account.main.name
quota = 50
enabled_protocol = "SMB"
}
# Create a Container App Environment
resource "azurerm_container_app_environment" "main" {
name = "${local.app_env_name}-env"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
}
# Connect the db storage share to the container app environment
resource "azurerm_container_app_environment_storage" "db" {
name = local.storage_mount_db_name
container_app_environment_id = azurerm_container_app_environment.main.id
account_name = azurerm_storage_account.main.name
share_name = azurerm_storage_share.db.name
access_key = azurerm_storage_account.main.primary_access_key
access_mode = "ReadWrite"
}
# Connect the server storage share to the container app environment
resource "azurerm_container_app_environment_storage" "server" {
name = local.storage_mount_server_name
container_app_environment_id = azurerm_container_app_environment.main.id
account_name = azurerm_storage_account.main.name
share_name = azurerm_storage_share.server.name
access_key = azurerm_storage_account.main.primary_access_key
access_mode = "ReadWrite"
}
```
##### frontend.tf
```hcl
# frontend.tf
resource "azurerm_container_app" "twenty_front" {
name = local.front_app_name
container_app_environment_id = azurerm_container_app_environment.main.id
resource_group_name = azurerm_resource_group.main.name
revision_mode = "Single"
depends_on = [azurerm_container_app.twenty_server]
ingress {
allow_insecure_connections = false
external_enabled = true
target_port = 3000
transport = "http"
traffic_weight {
percentage = 100
latest_revision = true
}
}
template {
min_replicas = 1
# things starts to fail when using more than 1 replica
max_replicas = 1
container {
name = "twenty-front"
image = "docker.io/twentycrm/twenty-front:${local.front_tag}"
cpu = local.cpu
memory = local.memory
env {
name = "REACT_APP_SERVER_BASE_URL"
value = "https://${azurerm_container_app.twenty_server.ingress[0].fqdn}"
}
}
}
}
# Set CORS rules for frontend app using AzAPI
resource "azapi_update_resource" "cors" {
type = "Microsoft.App/containerApps@2023-05-01"
resource_id = azurerm_container_app.twenty_front.id
body = jsonencode({
properties = {
configuration = {
ingress = {
corsPolicy = {
allowedOrigins = ["*"]
}
}
}
}
})
depends_on = [azurerm_container_app.twenty_front]
}
```
##### backend.tf
```hcl
# backend.tf
# Create three random UUIDs
resource "random_uuid" "access_token_secret" {}
resource "random_uuid" "login_token_secret" {}
resource "random_uuid" "refresh_token_secret" {}
resource "azurerm_container_app" "twenty_server" {
name = local.server_name
container_app_environment_id = azurerm_container_app_environment.main.id
resource_group_name = azurerm_resource_group.main.name
revision_mode = "Single"
depends_on = [azurerm_container_app.twenty_db, azurerm_container_app_environment_storage.server]
ingress {
allow_insecure_connections = false
external_enabled = true
target_port = 3000
transport = "http"
traffic_weight {
percentage = 100
latest_revision = true
}
}
template {
min_replicas = 1
max_replicas = 1
volume {
name = "twenty-server-data"
storage_type = "AzureFile"
storage_name = local.storage_mount_server_name
}
container {
name = local.server_name
image = "docker.io/twentycrm/twenty-server:${local.server_tag}"
cpu = local.cpu
memory = local.memory
volume_mounts {
name = "twenty-server-data"
path = "/app/packages/twenty-server/.local-storage"
}
# Environment variables
env {
name = "IS_SIGN_UP_DISABLED"
value = false
}
env {
name = "SIGN_IN_PREFILLED"
value = false
}
env {
name = "STORAGE_TYPE"
value = "local"
}
env {
name = "STORAGE_LOCAL_PATH"
value = ".local-storage"
}
env {
name = "PG_DATABASE_URL"
value = "postgres://${local.db_user}:${local.db_password}@${local.db_app_name}:5432/default"
}
env {
name = "FRONT_BASE_URL"
value = "https://${local.front_app_name}"
}
env {
name = "ACCESS_TOKEN_SECRET"
value = random_uuid.access_token_secret.result
}
env {
name = "LOGIN_TOKEN_SECRET"
value = random_uuid.login_token_secret.result
}
env {
name = "REFRESH_TOKEN_SECRET"
value = random_uuid.refresh_token_secret.result
}
}
}
}
# Set CORS rules for server app using AzAPI
resource "azapi_update_resource" "server_cors" {
type = "Microsoft.App/containerApps@2023-05-01"
resource_id = azurerm_container_app.twenty_server.id
body = jsonencode({
properties = {
configuration = {
ingress = {
corsPolicy = {
allowedOrigins = ["*"]
}
}
}
}
})
depends_on = [azurerm_container_app.twenty_server]
}
```
##### database.tf
```hcl
# database.tf
resource "azurerm_container_app" "twenty_db" {
name = local.db_app_name
container_app_environment_id = azurerm_container_app_environment.main.id
resource_group_name = azurerm_resource_group.main.name
revision_mode = "Single"
depends_on = [azurerm_container_app_environment_storage.db]
ingress {
allow_insecure_connections = false
external_enabled = false
target_port = 5432
transport = "tcp"
traffic_weight {
percentage = 100
latest_revision = true
}
}
template {
min_replicas = 1
max_replicas = 1
container {
name = local.db_app_name
image = "docker.io/twentycrm/twenty-postgres:${local.db_tag}"
cpu = local.cpu
memory = local.memory
volume_mounts {
name = "twenty-db-data"
path = "/var/lib/postgresql/data"
}
env {
name = "POSTGRES_USER"
value = "postgres"
}
env {
name = "POSTGRES_PASSWORD"
value = "postgres"
}
env {
name = "POSTGRES_DB"
value = "default"
}
}
volume {
name = "twenty-db-data"
storage_type = "AzureFile"
storage_name = local.storage_mount_db_name
}
}
}
```
## Others
Please feel free to Open a PR to add more Cloud Provider options.