🏃‍♂️

n8n + CloudRunで社内用n8nのテスト運用環境を作る

に公開

社内でn8nを使ってみようと思ってまずはローカルで試していたんですが
Slackなど、Webhookを使って連携する場合にローカルだと不便なので
さくっとCloudRunに立ててしまうことにしました。

n8n とは?

https://n8n.io/

Flexible AI workflow automationfor technical teams

ということで、AIを使ったWorkflowを構築するアプリケーションです。

Best apps & software integrations | n8n

2025/06/17 時点で、 1039 個のIntegrationsがありました。

n8n Press | Automate without limits

Our name
It is "n8n" with all lower case letters. It can either be pronounced "n eight n" or "nodemation".

While looking for a good name for the project with a free domain, Jan quickly realized that all the good ones he could think of were already taken. So, in the end, he chose nodemation. 'node-' in the sense that it uses a Node-View and that it uses Node.js and '-mation' for 'automation' which is what the project is supposed to help with. However, he did not like how long the name was and could not imagine writing something that long every time in the CLI. That is when he ended up on 'n8n'.

Node.js + Automation = nodemation
読み方は エヌエイトエヌ or ノードメーション らしいですね。

構成

  • Database
    • 今回はSQLiteでやります
  • CloudRun
    • n8nのコンテナを動かす
  • GCS
    • CloudRunにマウントして使います
    • SQLiteもここに書き込むようにします

※今回はIAPで認証を入れてしまうと、そのままだとWebhookが受け取れなくなってしまうため、認証はn8nに任せることにします。

Terraform

今回はTerraformから環境構築とデプロイまでやってしまいます。

variable "gcp_project_id" {
  type = string
}

variable "gcp_region" {
  type = string
}

## ServiceAccount
resource "google_service_account" "n8n" {
  project      = var.gcp_project_id
  account_id   = "lecto-n8n"
  display_name = "n8n ServiceAccount"
}

## GCS
resource "google_storage_bucket" "n8n_data" {
  project  = var.gcp_project_id
  location = var.gcp_region
  name     = "lecto-n8n-data"

  uniform_bucket_level_access = true
}

resource "google_storage_bucket_iam_member" "n8n_gcs_access" {
  bucket = google_storage_bucket.n8n_data.name
  role   = "roles/storage.objectUser"
  member = google_service_account.n8n.member
}


## CloudRun
locals {
  n8n_name        = "n8n"
  n8n_port        = 5678
  n8n_user_folder = "/home/node/.n8n"
  # 決定論的URL
  # https://cloud.google.com/run/docs/triggering/https-request?hl=ja#deterministic
  n8n_host        = "${local.n8n_name}-${data.google_project.project.number}.${var.gcp_region}.run.app"
}

data "google_project" "project" {
  project_id = var.gcp_project_id
}

resource "google_cloud_run_v2_service" "n8n" {
  project      = data.google_project.project.project_id
  provider     = google-beta
  name         = local.n8n_name
  location     = var.gcp_region
  launch_stage = "GA" # IAP使うときは "BETA"
  # iap_enabled  = true # 後で設定

  template {
    containers {
      image = "n8nio/n8n:1.98.1"

      ports {
        container_port = local.n8n_port
      }

      resources {
        limits = {
          cpu    = "1"
          memory = "2Gi"
        }
      }

      env {
        name  = "DB_TYPE"
        value = "sqlite" # postgresにする場合はここを変更
      }

      env {
        name  = "N8N_USER_FOLDER"
        value = local.n8n_user_folder
      }

      env {
        name  = "N8N_PROTOCOL"
        value = "https"
      }

      env {
        name  = "N8N_PORT"
        value = local.n8n_port
      }

      env {
        name  = "N8N_HOST"
        value = local.n8n_host
      }

      # デフォルトだとUI上に表示されるwebhookのURLにポート番号(5678)が入ってしまい、そのままコピーしても使えないので、ここで上書きする
      env {
        name  = "WEBHOOK_URL"
        value = "https://${local.n8n_host}"
      }

      volume_mounts {
        name       = "n8n-data"
        mount_path = local.n8n_user_folder
      }
    }

    service_account = google_service_account.n8n.email

    # gcs volume
    volumes {
      name = "n8n-data"
      gcs {
        bucket = google_storage_bucket.n8n_data.name
      }
    }

    scaling {
      min_instance_count = 1
      max_instance_count = 1
    }
  }
}

# 今回はIAPでの制限を入れないので、未認証でもアクセスできるようにします
# https://cloud.google.com/run/docs/authenticating/public?hl=ja#assign-allusers-iam-invoker
resource "google_cloud_run_service_iam_binding" "n8n" {
  location = google_cloud_run_v2_service.n8n.location
  service  = google_cloud_run_v2_service.n8n.name
  role     = "roles/run.invoker"
  members = [
    "allUsers"
  ]
}

これを apply します

n8nの初期ユーザー作成

https://{CloudRunのname}.{GCPプロジェクト番号}-{リージョン}.run.app を開いて、初期ユーザーを作成します。
※EmailとPasswordを設定

TODO

  • ちゃんと運用するならオートスケールさせたいので、PostgreSQLに移行する
  • 入退社があると面倒なので、IAPで認証かけておく(IAPかけてもSlackなどのWebhookを受け取れるようにする)

Discussion