# Manual Setup Guide This document contains detailed manual configuration instructions for LetsBe infrastructure components. > **Note**: Most of these steps are handled automatically by the deployment scripts. This guide is for manual intervention, troubleshooting, and advanced configuration. --- ## Table of Contents - [Docker Compose Updates](#updating-any-app-with-docker-compose) - [DNS Configuration](#domain-dns-entries) - [Required Subdomains](#required-subdomains-a-records) - [Post-Installation Admin Setup](#after-installation) - [Email Server Configuration](#e-mails-dns-settings-example) - [Tool-Specific Setup](#tool-specific-setup) - [Calcom](#calcom-setup-post-ansible) - [Baserow](#baserow-setup) - [Chatwoot](#chatwoot-setup) - [MinIO](#minio) - [Getmail6](#setting-up-getmail6) - [Nextcloud](#upgrade-nextcloud-over-docker-exec) - [Whiteboard](#whiteboard) - [Typebot](#typebot-installation) - [WordPress](#setting-up-wordpress-site-on-root-domain) - [SSL Certificates](#finding-certificates-with-certbot) - [Nginx Configuration Examples](#nginx-configuration-examples) --- ## Updating Any App With Docker Compose ```bash docker compose down && docker compose pull && docker image prune -f && docker compose up -d ``` ## Public Key Before running the start.sh script ensure the file "id_ed25519.key" is in the same path as the README.md here or add / decrypt. Always keep the file "id_ed25519.key" secure as you can access all server with all permissions with it. ## Domain DNS Entries Preset subdomains can be adjusted before executing the start.sh script under "env_setup.sh" part: "# Start - Set subdomain per tool". Note that you cannot use the same domain name more than once, each tool must have its own subdomain name. Each subdomain must then be added as an "A" DNS record in the domain settings, with the Server IP that you receive after purchasing a new server for the target customer. You should create the "A" DNS entries before starting the "start.sh" script and then wait at least 15 minutes before you go on. ## Redash First Installation Make sure to uncomment ports in docker-compose. ## Required Subdomains (A Records) Set up these A records before running the installation script. All should point to your server's IP address. ### Core Subdomains 1. {root} - Main domain (e.g., client.com) 2. www - Standard www prefix ### Tools & Applications 3. activepieces - Workflow automation (automation.client.com) 4. baserow - Database management (database.client.com) 5. calcom - Appointment scheduling (bookings.client.com) 6. chatwoot - Customer support (support.client.com) 7. collabora - Document collaboration (collabora.client.com) 8. documenso - Document signing (documenso.client.com) 9. ghost - Content management/blogging (blog.client.com) 10. gitea - Code repository (code.client.com) 11. glitchtip - Error tracking (debug.client.com) 12. html - Static website hosting (html.client.com) 13. keycloak - Authentication service (auth.client.com) 14. librechat - AI chat service (chat.client.com) 15. listmonk - Email newsletter service (newsletters.client.com) 16. nextcloud - File storage/collaboration (cloud.client.com) 17. nocodb - Low-code database (crm.client.com) 18. odoo - Business management (erp.client.com) 19. penpot - Design platform (design.client.com) 20. portainer - Container management (containers.client.com) 21. poste - Email server (mail.client.com) 22. redash - Data visualization (data.client.com) 23. squidex - Headless CMS (contenthub.client.com) 24. stirlingpdf - PDF manipulation (pdf.client.com) 25. typebot - Conversational forms (botlab.client.com and bots.client.com) 26. umami - Analytics (analytics.client.com) 27. uptime-kuma - Monitoring (uptime.client.com) 28. whiteboard - Collaborative whiteboarding (whiteboard.client.com) 29. windmill - Workflow automation (flows.client.com) 30. wordpress - Website/blog (www.client.com or client.com) ### Infrastructure Subdomains 31. minio - Object storage admin interface (minio.client.com) 32. s3 - S3-compatible storage endpoint (s3.client.com) 33. n8n - Workflow automation alternative (n8n.client.com) 34. ci - Continuous integration (for Gitea/Drone) (ci.client.com) 35. marketing - Optional marketing site (marketing.client.com) 36. helpdesk - Help desk/documentation (helpdesk.client.com) --- ## After Installation All tools must be prepared and the administrator account must be created/changed. This must be done immediately after installation: `*` = No Embedding for External Apps | Tool | Setup Required | |------|----------------| | baserow | Admin account must be created via the website | | *calcom | Admin account must be created via the website | | *chatwoot | Need to be changed (probably default: Username: "john@acme.inc", Password: "Password1!") | | gitea | Admin account must be created via the website | | glitchtip | Admin account must be created via the website under register new account | | listmonk | Admin account details auto generated and can be found in initial_setup_backup.zip: stacks/listmonk/config.toml | | *n8n | Admin account must be created via the website | | nextcloud | Admin account details auto generated and can be found in initial_setup_backup.zip: stacks/nextcloud/docker-compose.yml under "app" & "environment" | | nextcloud collabora | Admin account details auto generated and can be found in initial_setup_backup.zip: stacks/nextcloud/docker-compose.yml under "collabora" & "environment" | | *odoo | Admin account must be created via the website | | penpot | Admin account must be created via the website under register new account | | *poste | Admin account must be created via the website | | *squidex | Admin account must be created via the website under register new account | | *umami | Need to be changed (default: Username: "admin", Password: "umami") | | *uptime-kuma | Admin account must be created via the website | | windmill | Need to be changed (default: E-Mail: "admin@windmill.dev", Password: "changeme") | | *wordpress | Admin account must be created via the website | **Additional Notes:** - Add typebot subdomains: botlab & bots - **Restart REDIS container for activepieces after installation!** - **UNCOMMENT MINIO SSL/HPARAM LINES IN NGINX AFTER SETUP!!** --- ## Calcom Setup Post-Ansible 1. Go to env and generate a new calendso encryption using: `openssl rand -base64 24` 2. Add `NEXT_PUBLIC_API_V2_URL=https://bookings.{domain}` --- ## Docker-Compose and Nginx Config Location - Docker compose: `/opt/letsbe/stacks/AppName` - Nginx: `/etc/nginx/sites-available` --- ## E-Mails DNS Settings Example ``` A, mail.mydomain.com MX, Name: @ Value: mail.mydomain.com (Priority: 10) TXT, Name: _dmarc.mydomain.com Value: v=DMARC1; p=none; rua=mailto:administrator@mydomain.com TXT, Name: @ Value: v=spf1 mx ~all TXT = DKIM Name & Value: (generate for root domain in poste) ``` **IF DNS MANAGEMENT IS WIX:** ``` TXT, Name: domain.com Value: v=spf1 mx a:mail.qluxurymedia.com a:support.qluxurymedia.com ~all ``` **Email Ports:** - IMAP without SSL (unencrypted): 143 - IMAP with SSL (encrypted): 993 - SSL/TLS Outgoing: 465 > **IMPORTANT**: You MUST request another certificate in the TLS settings for Poste in order for IMAP to work! --- ## For Containers - `127.0.0.1` = local - `0.0.0.0` = network wide --- ## Clients with Existing Email Inboxes 1. Run setup of server, ensure client inserts mail.domain.com A level record first (Only setup A level until after sync, then when you're ready to launch you do the rest), and that the SSL certificate is pulled and valid 2. Setup Poste mailserver 3. Ask client to change password on original mail server account (for their protection) 4. IMAP sync (Use internal container IP if hostnames are the same for the destination): ```bash imapsync --host1 oldmailserver.com --user1 olduser --password1 oldpassword \ --host2 newmailserver.com --user2 newuser --password2 newpassword ``` 5. Once synced, change existing DNS records and create new ones and delete existing conflicting ones - MX records are vital here, do NOT change them until the new server is ready for use --- ## Collabora URL Example for Nextcloud ``` https://admin:password@collabora.mydomain.com ``` --- ## Tool-Specific Setup ### Whiteboard > DO NOT install whiteboard before setting it up properly If mimetypealiases not found: ```bash docker exec -u 33 -it customer-nextcloud-app php /var/www/html/occ maintenance:mimetype:update-db docker exec -u 33 -it customer-nextcloud-app php /var/www/html/occ maintenance:mimetype:update-js ``` **Setup Steps:** 1. Setup docker-compose and nginx if you haven't already made them standard 2. Generate JWT token: `openssl rand -base64 32` 3. Set that token for nextcloud and in the docker compose for the whiteboard: ```bash docker exec -u 33 -it {CUSTOMER}-nextcloud-app php /var/www/html/occ config:app:set whiteboard jwt_secret_key --value="your-random-key" ``` 4. Make NGINX config and link with: ```bash sudo ln -s /etc/nginx/sites-available/whiteboard.conf /etc/nginx/sites-enabled/ ``` 5. Generate SSL 6. Restart nginx and reload docker compose **Known Issues:** If the integration_whiteboard app was previously installed there might be a leftover non-standard mimetype configured. In this case opening the whiteboard may fail and a file is downloaded instead. Make sure to remove any entry in config/mimetypealiases.json mentioning whiteboard and run: ```bash docker exec -u 33 -it customer-nextcloud-app php /var/www/html/occ maintenance:mimetype:update-js -vvv docker exec -u 33 -it customer-nextcloud-app php /var/www/html/occ maintenance:mimetype:update-db -vvv ``` --- ### Finding Certificates with Certbot ```bash sudo certbot certificates ``` **Example (portnimara):** ```bash sudo certbot --nginx --expand -d portnimara.com -d www.portnimara.com \ -d analytics.portnimara.com \ -d automation.portnimara.com \ -d cloud.portnimara.com \ -d code.portnimara.com \ -d collabora.portnimara.com \ -d contenthub.portnimara.com \ -d crm.portnimara.com \ -d database.portnimara.com \ -d debug.portnimara.com \ -d design.portnimara.com \ -d flows.portnimara.com \ -d helpdesk.portnimara.com \ -d html.portnimara.com \ -d listmail.portnimara.com \ -d mail.portnimara.com \ -d marketing.portnimara.com \ -d schedule.portnimara.com \ -d support.portnimara.com \ -d uptime.portnimara.com \ -d whiteboard.portnimara.com ``` > **IMPORTANT**: DO NOT REQUEST CERTIFICATES BEFORE DOING THE LN COMMAND TO CONNECT SITES-AVAILABLE AND SITES-ENABLED --- ### Upgrade Nextcloud over Docker Exec Find docker-compose files: ```bash sudo find / -name "docker-compose.yml" ``` Docker composes are found in `/opt/letsbe/stacks/AppName` ```bash sudo docker exec --user www-data exampleCustomer-nextcloud-app php occ upgrade docker exec -u www-data -it exampleCustomer-nextcloud-app php occ db:add-missing-indices # (CHANGE IMAGE IN DOCKER COMPOSE FILE) docker-compose pull && docker-compose up -d ``` **Best Practice:** ```bash sudo docker compose down && sudo docker compose pull && sudo docker compose up -d && sudo docker compose logs -f {customer}-nextcloud-app ``` If stuck in maintenance mode: ```bash sudo docker exec --user www-data {customer}-nextcloud-app php occ maintenance:mode --off ``` Remove old images: ```bash docker image prune -f ``` THEN - do the docker exec commands listed above (occ upgrade and add missing indices) ### Nextcloud Webhook Listeners ```bash occ app:enable webhook_listeners ``` --- ### Setting Up WordPress Site on Root Domain Go to sites-available in the `/etc/nginx/sites-available` directory and edit the wordpress.conf: ```bash sudo nano wordpress.conf ``` Add new server block to point to root domain: ```nginx # Redirect HTTP to HTTPS for pitzone.io and www.pitzone.io server { listen 80; server_name {domain}.{suffix} www.{domain}.{suffix}; # Redirect all HTTP requests to HTTPS return 301 https://$host$request_uri; } # HTTPS configuration for pitzone.io with Proxy to Dockerized WordPress server { listen 443 ssl http2; server_name {domain} www.{domain}; # SSL certificate paths from Let's Encrypt ssl_certificate /etc/letsencrypt/live/{domain}/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/{domain}/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # Proxy requests to the Dockerized WordPress ({customer-wordpress} container) location / { proxy_pass http://{container_ip}:80; # Proxy to the Docker container's internal web server # TO FIND: sudo docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' {customer}-wordpress proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Deny access to .htaccess files, if any location ~ /\.ht { deny all; } # ACME challenge for SSL certificate renewal location ^~ /.well-known/acme-challenge/ { alias /var/www/html/.well-known/acme-challenge/; default_type "text/plain"; allow all; } } ``` Then request certificates with certbot: ```bash sudo certbot --nginx -d {domain} -d www.{domain} ``` **Nginx Errors:** ```bash sudo tail -f /var/log/nginx/error.log ``` **Check for auto renewal of certificates:** ```bash sudo certbot renew --dry-run ``` --- ### Baserow Setup 1. Go back into docker-compose after initial setup and mailserver setup 2. Update email settings to enable automated emails Example environment configuration: ```yaml environment: - BASEROW_PUBLIC_URL=https://database.qluxurymedia.com - DATABASE_URL=postgresql://bzcdowdmi7:xKaDO81KzhRu7yZ4KWjO@baserow-db:5432/baserow - EMAIL_SMTP=True - EMAIL_SMTP_USE_TLS=True - EMAIL_SMTP_HOST=mail.qluxurymedia.com - EMAIL_SMTP_PORT=587 - FROM_EMAIL=system@qluxurymedia.com - EMAIL_SMTP_USER=system@qluxurymedia.com - EMAIL_SMTP_PASSWORD= ``` --- ### Chatwoot Setup > For all passwords, use only letters and numbers here 1. Setup the mail server and create a "support@[domain]" email address, also ensure minio is an A record 2. Change the .env file to include the password for the support email (you have to set this up when you setup the mail address). The .env file can be found in `/opt/letsbe/stacks/chatwoot` 3. Change the .env file to allow users to setup accounts (set to true), set mailer_inbound_email_domain to top level domain (letsbe.biz, for example) 4. Enter the container using: ```bash docker exec -it [customername]-chatwoot-rails sh ``` 5. Run each of these: ```bash bundle exec rails db:setup bundle exec rails db:migrate ``` 6. Restart container (MUST BE DONE IN THE DIRECTORY OF THE DOCKER COMPOSE FILE): ```bash docker-compose down && docker-compose up -d ``` 7. Install and configure getmail6 and MinIO for S3 storage #### Updating Chatwoot ```bash docker compose down && docker compose pull && docker image prune -f && docker compose up -d docker compose run --rm rails bundle exec rails db:chatwoot_prepare ``` --- ### MinIO 1. Edit docker compose for chatwoot to include MinIO (right above "networks" at the bottom), generate MinIO Root User & Root Password: ```yaml minio: image: quay.io/minio/minio:RELEASE.2024-10-29T16-01-48Z container_name: {client}-minio restart: always volumes: - {client}-minio-data:/data environment: - MINIO_ROOT_USER={minioadminUsername} - MINIO_ROOT_PASSWORD={minioAdminPassword} command: server /data --console-address ":9001" ports: - "127.0.0.1:9000:9000" - "127.0.0.1:9001:9001" networks: matt-chatwoot: ipv4_address: 172.20.1.6 createbuckets: image: minio/mc depends_on: - minio entrypoint: > /bin/sh -c " sleep 10; mc alias set minio http://minio:9000 {MinIO Admin User} {MinIO Admin Password}; mc mb minio/typebot; mc anonymous set public minio/typebot/public; echo '{ \"Version\": \"2012-10-17\", \"Statement\": [{ \"Sid\": \"PublicRead\", \"Effect\": \"Allow\", \"Principal\": \"*\", \"Action\": [\"s3:GetBucketLocation\", \"s3:ListBucket\", \"s3:GetObject\"], \"Resource\": [\"arn:aws:s3:::typebot\", \"arn:aws:s3:::typebot/*\"], \"Condition\": {} }] }' > /tmp/policy.json mc admin policy add minio public-read /tmp/policy.json; mc admin policy set minio public-read user=minioadmin; echo '{ \"CORSRules\": [{ \"AllowedHeaders\": [\"*\"], \"AllowedMethods\": [\"PUT\", \"POST\", \"GET\"], \"AllowedOrigins\": [\"*\"], \"ExposeHeaders\": [\"ETag\"] }] }' > /tmp/cors.json; mc cors set minio/typebot /tmp/cors.json; exit 0; " networks: {chatwoot network name}: ``` And add to "Volumes" at the bottom: ```yaml {client}-minio-data: ``` 2. Edit .env file: ```bash ENABLE_ACCOUNT_SIGNUP=true SMTP_PASSWORD= SMTP_PORT=587 MAILER_INBOUND_EMAIL_DOMAIN=[customer_domain] # letsbe.solutions RAILS_INBOUND_EMAIL_PASSWORD=[SET] # set this password, you'll need it for getmail6 # Storage ACTIVE_STORAGE_SERVICE=s3_compatible STORAGE_BUCKET_NAME=chatwoot STORAGE_ACCESS_KEY_ID=[minioRootUser] STORAGE_SECRET_ACCESS_KEY=[minioRootPassword] STORAGE_REGION=eu-central STORAGE_ENDPOINT=https://minio.[domain.com] STORAGE_FORCE_PATH_STYLE=true # Amazon S3 S3_BUCKET_NAME=chatwoot AWS_ACCESS_KEY_ID=[minioRootUser] AWS_SECRET_ACCESS_KEY=[minioRootPassword] AWS_REGION=eu-central AWS_ENDPOINT=https://minio.[domain.com] AWS_FORCE_PATH_STYLE=true ``` 3. Go to `/etc/nginx/sites-available` and make a new minio.conf file (see Nginx Configuration Examples below) 4. Make s3.conf for the s3 subdomain (see Nginx Configuration Examples below) 5. **DO THIS AFTER YOU REQUEST THE CERTS:** ```bash sudo ln -s /etc/nginx/sites-available/minio.conf /etc/nginx/sites-enabled/ sudo ln -s /etc/nginx/sites-available/s3.conf /etc/nginx/sites-enabled/ ``` 6. Request certificate, restart nginx: ```bash certbot certificates sudo certbot --expand -d analytics.qluxurymedia.com,automation.qluxurymedia.com,...,minio.qluxurymedia.com,s3.qluxurymedia.com ``` 7. Chatwoot .env changes: - Set `RAILS_INBOUND_EMAIL_SERVICE=relay` - Add MinIO config - Set `RAILS_INBOUND_EMAIL_PASSWORD` to something secure (to use in the configuration for getmail6) - Change storage from "local" - add the MINIO configs **Example .env:** ```bash MAILER_SENDER_EMAIL=LetsBe SMTP_DOMAIN=mail.letsbe.solutions SMTP_ADDRESS=mail.letsbe.solutions SMTP_PORT=465 SMTP_USERNAME=support@letsbe.solutions SMTP_PASSWORD=3eA0GmDttFISUJ9qA0xb94YQz4N9tHObvDq5oBeX7BHg3OK42 SMTP_AUTHENTICATION=login SMTP_ENABLE_STARTTLS_AUTO=true SMTP_OPENSSL_VERIFY_MODE=none SMTP_TLS=true SMTP_SSL= MAILER_INBOUND_EMAIL_DOMAIN=letsbe.solutions RAILS_INBOUND_EMAIL_SERVICE=relay RAILS_INBOUND_EMAIL_PASSWORD=hDZn9KAHlGFDxeyh1z3o67H3971oh4yfmWDTJR9DrTn9dozwu ACTIVE_STORAGE_SERVICE=s3_compatible STORAGE_BUCKET_NAME=chatwoot STORAGE_ACCESS_KEY_ID=minioadmin STORAGE_SECRET_ACCESS_KEY=MfHt6x7H9rbfORhkfy9O51OXjFxsC4QHJcobKbs14CMvXJ820 STORAGE_REGION=eu-central STORAGE_ENDPOINT=https://s3.[domain].com STORAGE_FORCE_PATH_STYLE=true ``` 8. Access MinIO Interface and use the root user and password to login 9. Create a bucket called "chatwoot" 10. Check .env variables in the customer-chatwoot-rails container (OPTIONAL): ```bash docker exec -it {customer}-chatwoot-rails sh env | grep -E 'AWS|ACTIVE_STORAGE|STORAGE' exit ``` --- ### Setting up Getmail6 1. Install getmail6 on the server: ```bash sudo apt-get update sudo apt-get install -y python3-pip cd / git clone https://github.com/getmail6/getmail6.git cd getmail6 sudo python3 setup.py install getmail --version ``` 2. Create a dedicated getmailuser user, and SWITCH TO THAT USER: ```bash sudo adduser getmailuser sudo su - getmailuser ``` 3. Create necessary directories: ```bash mkdir -p ~/.getmail mkdir -p ~/bin mkdir -p ~/logs ``` 4. Make sure your RAILS_INBOUND_EMAIL_PASSWORD (Ingress Password) set in the chatwoot .env file is ready 5. Create the import email script (1 per account, ingress password remains the same): ```bash nano ~/bin/import_mail_to_chatwoot.sh ``` Content: ```bash #!/bin/bash INGRESS_PASSWORD="{insert RAILS_INBOUND_EMAIL_PASSWORD here}" URL='http://0.0.0.0:3011/rails/action_mailbox/relay/inbound_emails' curl -sS -u "actionmailbox:{insert RAILS_INBOUND_EMAIL_PASSWORD here}" \ -A "Action Mailbox: curl relayer" \ -H "Content-Type: message/rfc822" \ --data-binary @- \ $URL ``` 6. Make the script executable: ```bash chmod +x ~/bin/import_mail_to_chatwoot.sh ``` 7. Create the getmailrc config file (1 per email channel): ```bash nano ~/.getmail/getmailrc ``` Content: ```ini [retriever1] type = MultidropIMAPSSLRetriever server = mail.letsbe.solutions username = support@letsbe.solutions password = 3eA0GmDttFISUJ9qA0xb94YQz4N9tHObvDq5oBeX7BHg3OK42 mailboxes = ("INBOX",) envelope_recipient = delivered-to:1 [retriever2] # Add if you have more mailboxes type = MultidropIMAPSSLRetriever server = mail.{domain} username = help@{domain} password = {Password} mailboxes = ("INBOX",) envelope_recipient = delivered-to:1 [destination] type = MDA_external path = /home/getmailuser/bin/import_mail_to_chatwoot.sh [options] verbose = 1 allow_root_commands = true read_all = false delete = false delivered_to = false received = false message_log = ~/.getmail/getmail.log message_log_syslog = false message_log_verbose = true ``` 8. Make config executable: ```bash chmod +x ~/.getmail/getmailrc ``` 9. Test Getmail manually: ```bash getmail --rcfile ~/.getmail/getmailrc --verbose ``` 10. Setup cron job: ```bash crontab -e ``` Add at the bottom: ``` */1 * * * * /usr/local/bin/getmail --rcfile /home/getmailuser/.getmail/* --quiet ``` 11. Use "exit" to exit the getmailuser 12. Setup IMAP settings in Chatwoot App (in channel settings) 13. Do not forward emails to the address provided by Chatwoot in the channel setup --- ### Nextcloud Context Chat See: https://docs.nextcloud.com/server/latest/admin_manual/ai/app_context_chat.html#ai-app-context-chat --- ### Apps Only for Tech Savvy People 1. gitea 2. Squidex 3. Windmill --- ### Typebot Installation > Make sure to enact relevant changes in Nginx/Docker compose for s3 and Minio respectively 1. Create directory: ```bash mkdir {client-typebot} in /opt/letsbe/stacks/ ``` 2. Create docker-compose.yml: ```yaml version: '3.3' volumes: {client}-typebot-db-data: services: {client}-typebot-db: image: postgres:16 restart: always volumes: - {client}-typebot-db-data:/var/lib/postgresql/data environment: - POSTGRES_DB=typebot - POSTGRES_PASSWORD=typebot healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 networks: {chatwoot_network_name}: ipv4_address: 172.20.1.7 typebot-builder: image: baptistearno/typebot-builder:latest restart: always depends_on: {client}-typebot-db: condition: service_healthy ports: - '9080:3000' extra_hosts: - 'host.docker.internal:host-gateway' env_file: /opt/letsbe/env/typebot.env networks: {chatwoot_network_name}: ipv4_address: 172.20.1.8 typebot-viewer: image: baptistearno/typebot-viewer:latest depends_on: {client}-typebot-db: condition: service_healthy restart: always ports: - '9081:3000' env_file: /opt/letsbe/env/typebot.env networks: {chatwoot_network_name}: ipv4_address: 172.20.1.9 networks: {chatwoot_network_name}: external: true ``` 3. Create typebot.env in `/opt/letsbe/env/`: Generate 64-bit key: ```bash openssl rand -base64 24 | tr -d '\n' ; echo ``` Content: ```bash ENCRYPTION_SECRET={key} DATABASE_URL=postgresql://postgres:typebot@typebot-db:5432/typebot NODE_OPTIONS=--no-node-snapshot NEXTAUTH_URL=https://botlab.letsbe.solutions NEXT_PUBLIC_VIEWER_URL=https://bots.letsbe.solutions DEFAULT_WORKSPACE_PLAN=UNLIMITED ADMIN_EMAIL=administrator@letsbe.solutions # SMTP Configuration SMTP_USERNAME=noreply@letsbe.biz SMTP_PASSWORD=NF5joaQH2Q SMTP_HOST=mail.letsbe.solutions SMTP_PORT=465 SMTP_SECURE=true NEXT_PUBLIC_SMTP_FROM="Typebot Notifications " SMTP_AUTH_DISABLED=false # S3 Configuration for MinIO S3_ACCESS_KEY=minioadmin S3_SECRET_KEY=A7wRC52q5BofIjnQFdOaM7aBScS6H7jY54u0aax4P1KCsceP5 S3_BUCKET=typebot S3_PORT= S3_ENDPOINT=s3.letsbe.solutions S3_SSL=true S3_REGION=eu-central S3_PUBLIC_CUSTOM_DOMAIN=https://s3.letsbe.solutions ``` 4. Create nginx configs for botlab and bots (see Nginx Configuration Examples below) 5. Requesting the certs: ```bash # Link sites-available to sites-enabled first sudo certbot --nginx -d botlab.{client}.{domain} -d bots.{client}.{domain} sudo nginx -t sudo systemctl reload nginx ``` --- ## Nginx Configuration Examples ### Whiteboard Config ```nginx server { if ($host = whiteboard.letsbe.solutions) { return 301 https://$host$request_uri; } client_max_body_size 64M; listen 80; server_name whiteboard.letsbe.solutions; location / { return 301 https://$host$request_uri; } location ^~ /.well-known/acme-challenge/ { alias /var/www/html/.well-known/acme-challenge/; default_type "text/plain"; allow all; } } server { client_max_body_size 64M; listen 443 ssl http2; server_name whiteboard.letsbe.solutions; ssl_certificate /etc/letsencrypt/live/whiteboard.letsbe.solutions/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/whiteboard.letsbe.solutions/privkey.pem; location / { proxy_pass http://0.0.0.0:4014; proxy_http_version 1.1; proxy_read_timeout 3600s; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; add_header X-Frontend-Host $host; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; } location ^~ /.well-known/acme-challenge/ { alias /var/www/html/.well-known/acme-challenge/; default_type "text/plain"; allow all; } } ``` ### MinIO Config ```nginx server { if ($host = minio.letsbe.solutions) { return 301 https://$host$request_uri; } client_max_body_size 64M; listen 80; server_name minio.letsbe.solutions; location / { return 301 https://$host$request_uri; } location ^~ /.well-known/acme-challenge/ { alias /var/www/html/.well-known/acme-challenge/; default_type "text/plain"; allow all; } } server { listen 443 ssl http2; server_name minio.letsbe.solutions; location / { proxy_pass http://0.0.0.0:9001; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded_Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } location ^~ /.well-known/acme-challenge/ { alias /var/www/html/.well-known/acme-challenge/; default_type "text/plain"; allow all; } ssl_certificate /etc/letsencrypt/live/support.letsbe.solutions/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/support.letsbe.solutions/privkey.pem; } ``` ### S3 Config ```nginx server { if ($host = s3.qluxurymedia.com) { return 301 https://$host$request_uri; } client_max_body_size 0; listen 80; server_name s3.qluxurymedia.com; location / { return 301 https://$host$request_uri; } location ^~ /.well-known/acme-challenge/ { alias /var/www/html/.well-known/acme-challenge/; default_type "text/plain"; allow all; } } server { client_max_body_size 0; listen 443 ssl http2; server_name s3.qluxurymedia.com; ssl_certificate /etc/letsencrypt/live/analytics.qluxurymedia.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/analytics.qluxurymedia.com/privkey.pem; location / { proxy_pass http://127.0.0.1:9000; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_hide_header Access-Control-Allow-Origin; add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always; add_header 'Access-Control-Allow-Headers' '*' always; add_header 'Access-Control-Expose-Headers' 'Origin, Content-Type, Content-MD5, Content-Disposition, ETag' always; if ($request_method = 'OPTIONS') { add_header 'Content-Length' 0; add_header 'Content-Type' 'text/plain; charset=utf-8'; return 204; } } location ^~ /.well-known/acme-challenge/ { alias /var/www/html/.well-known/acme-challenge/; default_type "text/plain"; allow all; } } ``` ### Typebot Botlab Config ```nginx server { if ($host = botlab.{client}.{domain}) { return 301 https://$host$request_uri; } client_max_body_size 64M; listen 80; server_name botlab.{client}.{domain}; location / { return 301 https://$host$request_uri; } location ^~ /.well-known/acme-challenge/ { alias /var/www/html/.well-known/acme-challenge/; default_type "text/plain"; allow all; } } server { client_max_body_size 64M; large_client_header_buffers 4 16k; listen 443 ssl http2; server_name botlab.{client}.{domain}; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; location / { proxy_pass http://172.20.1.8:3000; proxy_http_version 1.1; proxy_cache_bypass $http_upgrade; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } ``` ### Typebot Bots Config ```nginx server { if ($host = bots.{client}.{domain}) { return 301 https://$host$request_uri; } client_max_body_size 64M; listen 80; server_name bots.{client}.{domain}; location / { return 301 https://$host$request_uri; } location ^~ /.well-known/acme-challenge/ { alias /var/www/html/.well-known/acme-challenge/; default_type "text/plain"; allow all; } } server { client_max_body_size 64M; large_client_header_buffers 4 16k; listen 443 ssl http2; server_name bots.{client}.{domain}; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; location / { proxy_pass http://172.20.1.9:3000; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } ``` --- ## Nginx Tips Always do the link of the nginx files before requesting certs, but comment out any ssl locations. ```bash # Link sites-available to sites-enabled sudo ln -s /etc/nginx/sites-available/{config}.conf /etc/nginx/sites-enabled/ # Test nginx configuration sudo nginx -t # Reload nginx sudo systemctl reload nginx ``` --- ## ToDo - Fix backups.sh