# Deploying n8n on Oracle Kubernetes Engine (OKE)

This guide walks you through **deploying the n8n automation platform on Oracle Cloud Infrastructure (OCI)** using **Oracle Kubernetes Engine (OKE)** — including persistent storage, authentication, and public access via an OCI Load Balancer.

### Overview

**n8n** (“nodemation”) is an open-source workflow automation tool that lets you visually connect services and automate tasks.

**Oracle Kubernetes Engine (OKE)** is a managed Kubernetes service on OCI that provides scalable, highly available clusters with integrated networking and load balancing.

In this guide, you’ll:

* Create a namespace and persistent volume for n8n data
    
* Deploy n8n to OKE
    
* Expose it through an OCI LoadBalancer
    
* Fix permission and cookie issues for production readiness
    

### Prerequisites

Before you begin, ensure you have:

* An existing **OKE cluster** (v1.34.1 or newer)
    
* `kubectl` configured to connect to the cluster (`kubectl get nodes` works)
    
* One **public subnet OCID** (for the OCI Load Balancer)
    
* Basic understanding of Kubernetes YAML manifests
    

### Step 1 — Create the Namespace and Persistent Volume Claim

Create a namespace for organizational isolation and a PVC to persist n8n configuration and workflows.

```plaintext
apiVersion: v1
kind: Namespace
metadata:
  name: n8n
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: n8n-pvc
  namespace: n8n
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 50Gi
```

Apply it:

```plaintext
kubectl apply -f n8n-storage.yaml
```

### Step 2 — Create Authentication Secret

Create a Kubernetes secret for n8n’s basic authentication.

```plaintext
apiVersion: v1
kind: Secret
metadata:
  name: n8n-secret
  namespace: n8n
type: Opaque
stringData:
  username: admin
  password: strongpassword123
```

Apply it:

```plaintext
kubectl apply -f n8n-secret.yaml
```

### Step 3 — Deploy n8n Application

Below is a production-ready Deployment that:

* Uses the fully qualified Docker image
    
* Mounts the PVC for persistence
    
* Fixes file-permission issues (`fsGroup: 1000`)
    
* Disables secure cookie enforcement (for HTTP access via LoadBalancer)
    

```plaintext
apiVersion: apps/v1
kind: Deployment
metadata:
  name: n8n
  namespace: n8n
spec:
  replicas: 1
  selector:
    matchLabels:
      app: n8n
  template:
    metadata:
      labels:
        app: n8n
    spec:
      securityContext:
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
      containers:
        - name: n8n
          image: docker.io/n8nio/n8n:latest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 5678
          env:
            - name: N8N_BASIC_AUTH_ACTIVE
              value: "true"
            - name: N8N_BASIC_AUTH_USER
              valueFrom:
                secretKeyRef:
                  name: n8n-secret
                  key: username
            - name: N8N_BASIC_AUTH_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: n8n-secret
                  key: password
            - name: N8N_HOST
              value: "n8n"
            - name: N8N_PORT
              value: "5678"
            - name: N8N_PROTOCOL
              value: "http"
            - name: N8N_SECURE_COOKIE
              value: "false"
            - name: GENERIC_TIMEZONE
              value: "Asia/Kolkata"
            - name: TZ
              value: "Asia/Kolkata"
          volumeMounts:
            - name: n8n-data
              mountPath: /home/node/.n8n
      volumes:
        - name: n8n-data
          persistentVolumeClaim:
            claimName: n8n-pvc
```

Apply it:

```plaintext
kubectl apply -f n8n-deployment.yaml
```

### Step 4 — Expose n8n via OCI LoadBalancer

Now expose n8n externally so you can access it through a public IP.

```plaintext
apiVersion: v1
kind: Service
metadata:
  name: n8n-service
  namespace: n8n
spec:
  type: LoadBalancer
  selector:
    app: n8n
  ports:
    - name: http
      port: 80
      targetPort: 5678
      protocol: TCP
```

Apply it:

```plaintext
kubectl apply -f n8n-service.yaml
```

After a few minutes:

```plaintext
kubectl get svc -n n8n
NAME            TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
n8n-service     LoadBalancer   10.96.123.45   140.238.xxx.xxx  80:31234/TCP   2m
```

### Step 5 — Verify and Troubleshoot

Check pod logs:

```plaintext
kubectl logs -f -n n8n deploy/n8n
```

Expected startup log:

```plaintext
Editor is now accessible:
 ▸  http://0.0.0.0:5678/
```

If you see permission errors like `EACCES: permission denied`, confirm your `fsGroup: 1000` is present in the YAML.

If you get a “secure cookie” warning, ensure `N8N_SECURE_COOKIE=false` is set, or enable HTTPS on your LoadBalancer.

### Step 6 — (Optional) Add HTTPS with OCI LoadBalancer

For production, you can enable HTTPS by adding these annotations to the Service:

```plaintext
metadata:
  annotations:
    service.beta.kubernetes.io/oci-load-balancer-ssl-ports: "443"
    service.beta.kubernetes.io/oci-load-balancer-tls-secret: "n8n/n8n-tls"
```

Then create a TLS secret:

```plaintext
kubectl create secret tls n8n-tls \
  --cert=server.crt --key=server.key -n n8n
```

### Step 7 — (Optional) Use PostgreSQL for Production

For better performance and reliability, you can connect n8n to an external PostgreSQL (or OCI Autonomous Database):

```plaintext
- name: DB_TYPE
  value: "postgresdb"
- name: DB_POSTGRESDB_HOST
  value: "<your-db-host>"
- name: DB_POSTGRESDB_USER
  value: "<your-db-user>"
- name: DB_POSTGRESDB_PASSWORD
  value: "<your-db-password>"
- name: DB_POSTGRESDB_DATABASE
  value: "n8n"
```

![n8n](https://cdn.hashnode.com/res/hashnode/image/upload/v1762358844592/a815c23a-16f1-4a6b-8548-a4d2b09be778.png align="center")

### Summary

| Component | Purpose |
| --- | --- |
| **Namespace** | Isolates resources |
| **PVC** | Persists workflow data |
| **Secret** | Stores login credentials |
| **Deployment** | Runs n8n container securely |
| **Service (LoadBalancer)** | Exposes n8n externally |
| **SecurityContext** | Fixes PVC permission issues |
| **Optional TLS** | Enables HTTPS via OCI LoadBalancer |
