- Migrated developer docs to Twenty website - Modified User Guide and Docs layout to include sections and subsections **Section Example:** <img width="549" alt="Screenshot 2024-05-30 at 15 44 42" src="https://github.com/twentyhq/twenty/assets/102751374/41bd4037-4b76-48e6-bc79-48d3d6be9ab8"> **Subsection Example:** <img width="557" alt="Screenshot 2024-05-30 at 15 44 55" src="https://github.com/twentyhq/twenty/assets/102751374/f14c65a9-ab0c-4530-b624-5b20fc00511a"> - Created different components (Tabs, Tables, Editors etc.) for the mdx files **Tabs & Editor** <img width="665" alt="Screenshot 2024-05-30 at 15 47 39" src="https://github.com/twentyhq/twenty/assets/102751374/5166b5c7-b6cf-417d-9f29-b1f674c1c531"> **Tables** <img width="698" alt="Screenshot 2024-05-30 at 15 57 39" src="https://github.com/twentyhq/twenty/assets/102751374/2bbfe937-ec19-4004-ab00-f7a56e96db4a"> <img width="661" alt="Screenshot 2024-05-30 at 16 03 32" src="https://github.com/twentyhq/twenty/assets/102751374/ae95b47c-dd92-44f9-b535-ccdc953f71ff"> - Created a crawler for Twenty Developers (now that it will be on the twenty website). Once this PR is merged and the website is re-deployed, we need to start crawling and make sure the index name is ‘twenty-developer’ - Added a dropdown menu in the header to access User Guide and Developers + added Developers to footer https://github.com/twentyhq/twenty/assets/102751374/1bd1fbbd-1e65-4461-b18b-84d4ddbb8ea1 - Made new layout responsive Please fill in the information for each mdx file so that it can appear on its card, as well as in the ‘In this article’ section. Example with ‘Getting Started’ in the User Guide: <img width="786" alt="Screenshot 2024-05-30 at 16 29 39" src="https://github.com/twentyhq/twenty/assets/102751374/2714b01d-a664-4ddc-9291-528632ee12ea"> Example with info and sectionInfo filled in for 'Getting Started': <img width="620" alt="Screenshot 2024-05-30 at 16 33 57" src="https://github.com/twentyhq/twenty/assets/102751374/bc69e880-da6a-4b7e-bace-1effea866c11"> Please keep in mind that the images that are being used for Developers are the same as those found in User Guide and may not match the article. --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
437 lines
12 KiB
Plaintext
437 lines
12 KiB
Plaintext
---
|
||
title: Vendor-Specific Instructions
|
||
icon: TbCloud
|
||
image: /images/user-guide/notes/notes_header.png
|
||
---
|
||
|
||
<ArticleWarning>
|
||
This document is maintained by the community. It might contain issues.
|
||
Feel free to join our discord if you need assistance.
|
||
</ArticleWarning>
|
||
|
||
|
||
## Render
|
||
|
||
[](https://render.com/deploy?repo=https://github.com/twentyhq/twenty)
|
||
|
||
## RepoCloud
|
||
|
||
[](https://repocloud.io/details/?app_id=259)
|
||
|
||
|
||
## 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 "random_uuid" "file_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
|
||
}
|
||
env {
|
||
name = "FILE_TOKEN_SECRET"
|
||
value = random_uuid.file_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.
|