diff --git a/.github/workflows/kamal-deploy.yml b/.github/workflows/kamal-deploy.yml index bf9756b07a..0ec79b7310 100644 --- a/.github/workflows/kamal-deploy.yml +++ b/.github/workflows/kamal-deploy.yml @@ -81,6 +81,7 @@ jobs: DB_USER: ${{ secrets.DB_USER }} DB_PASSWORD: ${{ secrets.DB_PASSWORD }} DB_NAME: ${{ secrets.DB_NAME }} + REDIS_URL: ${{ secrets.REDIS_URL }} steps: - name: Checkout code diff --git a/.github/workflows/kamal-setup.yml b/.github/workflows/kamal-setup.yml index 70b42e6963..38b05715ad 100644 --- a/.github/workflows/kamal-setup.yml +++ b/.github/workflows/kamal-setup.yml @@ -78,6 +78,7 @@ jobs: DB_USER: ${{ secrets.DB_USER }} DB_PASSWORD: ${{ secrets.DB_PASSWORD }} DB_NAME: ${{ secrets.DB_NAME }} + REDIS_URL: ${{ secrets.REDIS_URL }} steps: - name: Checkout code diff --git a/kamal/README.md b/kamal/README.md index 6d1c9dbb6f..ba2ceaf0e8 100644 --- a/kamal/README.md +++ b/kamal/README.md @@ -1,6 +1,7 @@ # Kamal Setup -1. Initiate a Linux instance & Get it's +1. Initiate a Linux instance & Get it's + - Public IP address - SSH credentials @@ -13,43 +14,50 @@ 4. If the above returns a non-zero error code, run the below: - ```sh - eval `ssh-agent -s` - ssh-add .pem - ``` + ```sh + eval `ssh-agent -s` + ssh-add .pem + ``` 5. Now test the SSH status again: - ```sh - kamal lock status - ``` + ```sh + kamal lock status + ``` - This should now run successfully & exit with 0. + This should now run successfully & exit with 0. 6. Generate a Classic Personal Access Token for `container:write` & `container:read` for your Image Registry (DockerHub, GHCR, etc.) & add it to your environment variables (.env file). Also update the Registry details in the `deploy.yml`. 7. If your SSH user is a non-root user, run the below command to add the user to the docker group: - ```sh - sudo usermod -aG docker ${USER} - ``` + ```sh + sudo usermod -aG docker ${USER} + ``` - > Note: The above needs to be ran on the Cloud VM. There is an open Issue on Kamal for the same [here](https://github.com/basecamp/kamal/issues/405) + > Note: The above needs to be ran on the Cloud VM. There is an open Issue on Kamal for the same [here](https://github.com/basecamp/kamal/issues/405) > Run the below for SSL config the first time + ```sh sudo mkdir -p /letsencrypt && sudo touch /letsencrypt/acme.json && sudo chmod 600 /letsencrypt/acme.json ``` +> Run this command to create the private bridge network used by kamal to reference containers on one instance + +```sh +docker network create -d bridge private +``` + 8. Make sure you have docker buildx locally on your machine where you run the kamal CLI from! 9. Voila! You are all set to deploy your application to the cloud with Kamal! 🚀 - ```sh - kamal setup -c kamal/deploy.yml - ``` + ```sh + kamal setup -c kamal/deploy.yml + ``` - This will setup the cloud VM with all the necessary tools & dependencies to run your application. + This will setup the cloud VM with all the necessary tools & dependencies to run your application. > Make sure to run `kamal env push` before a `kamal deploy` to push the latest environment variables to the cloud VM. @@ -57,28 +65,29 @@ sudo mkdir -p /letsencrypt && sudo touch /letsencrypt/acme.json && sudo chmod 60 - If you run into an error such as: - ```sh - failed to solve: cannot copy to non-directory: - ``` + ```sh + failed to solve: cannot copy to non-directory: + ``` - Then simply run `pnpm clean` & try again. + Then simply run `pnpm clean` & try again. - Make sure your Database accepts connection from the cloud VM. You can do this by adding the VM's IP address to the `Allowed Hosts` in your Database settings. - If you get an error such as: - ```sh - Lock failed: failed to acquire lock: lockfile already exists - ``` + ```sh + Lock failed: failed to acquire lock: lockfile already exists + ``` - Then simply run `kamal lock release -c kamal/deploy.yml` & try again. + Then simply run `kamal lock release -c kamal/deploy.yml` & try again. - If you run into: - ```sh - No config found - ``` - Then simply add the following at the end of the command: `-c kamal/deploy.yml` + ```sh + No config found + ``` + + Then simply add the following at the end of the command: `-c kamal/deploy.yml` For further details, refer to the [Kamal Documentation](https://kamal-deploy.org/docs/configuration) or reach out to us on our [Discord](https://formbricks.com/discord) diff --git a/kamal/deploy.yml b/kamal/deploy.yml index c32448ab9c..63579dfbc3 100644 --- a/kamal/deploy.yml +++ b/kamal/deploy.yml @@ -9,17 +9,17 @@ servers: web: # Use a named role, so it can be used as entrypoint by Traefik hosts: - 18.196.187.144 + - 18.196.172.27 labels: - traefik.http.routers.formbricks-kamal.entrypoints: websecure - traefik.http.routers.formbricks-kamal.rule: Host(`app.formbricks.com`) - traefik.http.routers.formbricks-kamal.tls.certresolver: letsencrypt + traefik.http.routers.formbricks-kamal.entrypoints: web + options: + network: "private" # Credentials for your image host. registry: # Specify the registry server, if you're not using Docker Hub server: ghcr.io username: mattinannt - # Always use an access token rather than real password when possible. password: - KAMAL_REGISTRY_PASSWORD @@ -27,11 +27,8 @@ registry: # Inject ENV variables into containers (secrets come from .env). # Remember to run `kamal env push` after making changes! env: - # clear: - # DB_HOST: 192.168.0.2 clear: - REDIS_URL: redis://default:password@172.31.40.79:6379 - REDIS_HTTP_URL: http://172.31.40.79:7379 + REDIS_HTTP_URL: http://formbricks-kamal-webdis:7379 secret: - IS_FORMBRICKS_CLOUD - WEBAPP_URL @@ -97,6 +94,7 @@ env: - DB_PASSWORD - DB_NAME - SENTRY_AUTH_TOKEN + - REDIS_URL # Use a different ssh user than root ssh: @@ -115,123 +113,39 @@ builder: - NEXT_PUBLIC_SENTRY_DSN - ASSET_PREFIX_URL - SENTRY_AUTH_TOKEN - multiarch: false cache: type: registry options: mode=max,image-manifest=true,oci-mediatypes=true -# secrets: -# - GITHUB_TOKEN -# remote: -# arch: amd64 -# host: ssh://app@192.168.0.1 traefik: - options: - publish: - - "443:443" - volume: - - "/letsencrypt/acme.json:/letsencrypt/acme.json" # To save the configuration file. args: entryPoints.web.address: ":80" - entryPoints.websecure.address: ":443" - entryPoints.web.http.redirections.entryPoint.to: websecure - entryPoints.web.http.redirections.entryPoint.scheme: https - entryPoints.web.http.redirections.entrypoint.permanent: true - entrypoints.websecure.http.tls: true - entrypoints.websecure.http.tls.domains[0].main: "app.formbricks.com" - entrypoints.websecure.http.tls.domains[0].sans: "*.formbricks.com" - certificatesResolvers.letsencrypt.acme.email: "hola@formbricks.com" - certificatesResolvers.letsencrypt.acme.storage: "/letsencrypt/acme.json" - certificatesresolvers.letsencrypt.acme.dnschallenge.provider: cloudflare - env: - secret: - - CLOUDFLARE_DNS_API_TOKEN - - CLOUDFLARE_EMAIL + options: + network: "private" # Use accessory services (secrets come from .env). accessories: - # db: - # image: mysql:8.0 - # host: 192.168.0.2 - # port: 3306 - # env: - # clear: - # MYSQL_ROOT_HOST: '%' - # secret: - # - MYSQL_ROOT_PASSWORD - # files: - # - config/mysql/production.cnf:/etc/mysql/my.cnf - # - db/production.sql:/docker-entrypoint-initdb.d/setup.sql - # directories: - # - data:/var/lib/mysql - redis: - image: redis:7.0 - host: 18.196.187.144 - port: "172.31.40.79:6379:6379" - directories: - - data:/data - webdis: image: nicolas/webdis:0.1.22 - host: 18.196.187.144 - cmd: > - sh -c " - wget -O /usr/local/bin/webdis.json https://github.com/nicolasff/webdis/raw/0.1.22/webdis.json && - awk '/\"redis_host\":/ {print \"\\t\\\"redis_host\\\": \\\"172.31.40.79\\\",\"; next} /\"logfile\":/ {print \"\\t\\\"logfile\\\": \\\"/dev/stderr\\\"\"; next} {print}' /usr/local/bin/webdis.json > /usr/local/bin/webdis_modified.json && - mv /usr/local/bin/webdis_modified.json /usr/local/bin/webdis.json && - /usr/local/bin/webdis /usr/local/bin/webdis.json" - - port: "172.31.40.79:7379:7379" - directories: - - data:/data - - pgbouncer: - image: edoburu/pgbouncer:latest - host: 18.196.187.144 - port: "172.31.40.79:5432:5432" + roles: + - web env: - clear: - LISTEN_PORT: "5432" - POOL_MODE: "transaction" - MAX_CLIENT_CONN: "300" - DEFAULT_POOL_SIZE: "100" - AUTH_TYPE: "scram-sha-256" secret: - - DB_USER - - DB_PASSWORD - - DB_HOST - - DB_NAME - -# Configure custom arguments for Traefik -# traefik: -# args: -# accesslog: true -# accesslog.format: json + - REDIS_URL + cmd: > + /bin/sh -c ' + REDIS_HOST=$(echo $REDIS_URL | awk -F "[:/]" "{print \$4}") && + wget -O /usr/local/bin/webdis.json https://github.com/nicolasff/webdis/raw/0.1.22/webdis.json && + sed -i "s/\"redis_host\":.*/\"redis_host\": \"$REDIS_HOST\",/" /usr/local/bin/webdis.json && + sed -i "s/\"logfile\":.*/\"logfile\": \"\/dev\/stderr\"/" /usr/local/bin/webdis.json && + /usr/local/bin/webdis /usr/local/bin/webdis.json' + port: 7379 + options: + network: "private" healthcheck: path: /health port: 3000 max_attempts: 15 interval: 20s -# Bridge fingerprinted assets, like JS and CSS, between versions to avoid -# hitting 404 on in-flight requests. Combines all files from new and old -# version inside the asset_path. -# asset_path: /rails/public/assets - -# Configure rolling deploys by setting a wait time between batches of restarts. -# boot: -# limit: 10 # Can also specify as a percentage of total hosts, such as "25%" -# wait: 2 - -# Configure the role used to determine the primary_host. This host takes -# deploy locks, runs health checks during the deploy, and follow logs, etc. -# -# Caution: there's no support for role renaming yet, so be careful to cleanup -# the previous role on the deployed hosts. -# primary_role: web - -# Controls if we abort when see a role with no hosts. Disabling this may be -# useful for more complex deploy configurations. -# -# allow_empty_roles: false