# Deploying Apidog on Kubernetes using Deployment Manifest

## Overview
This guide details the deployment of Apidog Enterprise using Kubernetes deployment manifests (`.yaml`).

:::tip[Recommendation]
We strongly suggest using the [Deploying Apidog on Kubernetes using Helm](https://self-hosting.apidog.com/deploying-apidog-on-kubernetes-using-helm-1860587m0.md) for easier lifecycle management. Use this YAML approach only if Helm is not feasible in your environment.
:::

## Requirements & Prerequisites

### System Requirements

    - **[Kubernetes](https://kubernetes.io/docs/setup/)** Version 1.19+ (Verify with `kubectl version`)


    - **Hardware & Software:** For hardware and software requirement, please refer to the [System Requirements](https://self-hosting.apidog.com/system-requirements-1048815m0.md) documentation.

### External Dependencies
    - **Database:** A PostgreSQL or MySQL instance. See [Database Configuration](https://self-hosting.apidog.com/database-configuration-405309m0.md).
    - **Storage**: An S3-compatible object storage service. See [Storage Services Configuration](https://self-hosting.apidog.com/storage-services-configuration-405310m0.md)
    - **Docker Registry Access:** Ensure you have the Access Token (received via email) to pull the private image.
    
    
## Preparation: Image Pull

Kubernetes requires authentication to pull images from the private Docker Hub repository.

 1.  **Log in to Docker Hub manually to verify credentials**
 
 ```bash
 docker login --username=apidog docker.io
 ```
 2. **Pull the Image**
 ```bash
 docker pull docker.io/apidog/apidog-ee:<image_tag>
 ```

## Database Initialization
:::info[]
**This guide utilizes a MySQL database. If you are using PostgreSQL, please refer to the [PostgreSQL Guidelines](https://self-hosting.apidog.com/database-configuration-405309m0.md##postgresql)**
:::

Apidog does not automatically create the database. You must manually connect to your database instance to initialize the database. Once connected, execute:

```sql
CREATE DATABASE IF NOT EXISTS apidog CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
exit;
```

## Installation Steps

1. **Create Kubernetes Secrets:**

Security best practices dictate that sensitive data (passwords, tokens) should not be stored in plain text within the Deployment YAML. Create a generic secret named apidog-secrets first.

```bash
kubectl create secret generic apidog-secrets \
  --from-literal=pg-password='<postgres_password>' \
  --from-literal=mysql-password='<mysql_password>' \
  --from-literal=redis-password='<redis_password>' \
  --from-literal=jwt-secret='<JWT secret>' \
  --from-literal=admin-password='<admin_password>' \
  --from-literal=mailer-password='<mailer_password>' \
  --from-literal=storage-access-key='<storage_access_key>' \
  --from-literal=storage-access-secret='<storage_access_secret>'
```

:::tip[For using SSO or RTM, add additional secrets:] 

- `--from-literal=oauth2-client-secret='<client_secret>'` (for OAuth2 SSO)
- `--from-literal=okta-client-secret='<client_secret>'` (for Okta SSO)
- `--from-literal=ldap-bind-password='<bind_password>'` (for LDAP SSO)
- `--from-literal=rtm-redis-password='<rtm_redis_password>'` (for RTM with separate Redis)
:::


2. **Configure Deployment Manifest:**
  
     Save the following content as `deployment.yaml`. Edit the `deployment.yaml` file to match your environment. You must update:
    - **Database Connections**: Host, Port, Username, Password.
    - **Redis Settings**: Host, Port, Auth.
    - **Base URL**: The domain where Apidog will be accessible.
    - **License**
    
    and other environment specific values.
    
    :::caution[Important]
    For more information on the environment variables and how to configure them, please refer to the [Configuration Guide](doc-405300)
    :::
    
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: apidog
  labels:
    app: apidog
spec:
  replicas: 1
  selector:
    matchLabels:
      app: apidog
  template:
    metadata:
      labels:
        app: apidog
    spec:
      securityContext:
        runAsNonRoot: true
        # Uncomment the following if you encounter permission issues:
        # runAsUser: 1001
      containers:
        - name: apidog
          image: docker.io/apidog/apidog-ee:<image_tag>
          imagePullPolicy: IfNotPresent
          securityContext:
            runAsNonRoot: true
            # Uncomment the following if you encounter permission issues:
            # runAsUser: 1001
            allowPrivilegeEscalation: false
            capabilities:
              drop:
                - ALL
            privileged: false
            seccompProfile:
              type: RuntimeDefault
          resources:
            limits:
              memory: "8Gi"
              cpu: 4
            requests:
              memory: "4Gi"
              cpu: 1
          env:
            # Database configuration
            - name: DB_DIALECT
              value: "mysql"  # Change to "postgresql" if using PostgreSQL
            # PostgreSQL configuration (if DB_DIALECT is postgresql)
            - name: PG_DATABASE
              value: "apidog"
            - name: PG_HOST
              value: "<postgres_host>"
            - name: PG_PORT
              value: "<postgres_port>"
            - name: PG_USERNAME
              value: "<postgres_username>"
            - name: PG_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: apidog-secrets
                  key: pg-password
            - name: PG_TLS_REJECT_UNAUTHORIZED
              value: "false"
            # MySQL configuration (if DB_DIALECT is mysql)
            - name: MYSQL_DATABASE
              value: "apidog"
            - name: MYSQL_HOST
              value: "<mysql_host>"
            - name: MYSQL_PORT
              value: "<mysql_port>"
            - name: MYSQL_USER_NAME
              value: "<mysql_username>"
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: apidog-secrets
                  key: mysql-password
            # Redis configuration
            - name: REDIS_HOST
              value: "<redis_host>"
            - name: REDIS_PORT
              value: "<redis_port>"
            - name: REDIS_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: apidog-secrets
                  key: redis-password
            - name: REDIS_DB
              value: "<redis_db>"
            - name: REDIS_TLS_ENABLED
              value: "false"
            # RTM (Collaboration and Runner Service) configuration - Optional
            # Uncomment to enable RTM queue:
            # - name: RTM_QUEUE_ENABLE
            #   value: "true"
            # If using separate Redis for RTM, uncomment and configure:
            # - name: RTM_REDIS_HOST
            #   value: "<rtm_redis_host>"
            # - name: RTM_REDIS_PORT
            #   value: "<rtm_redis_port>"
            # - name: RTM_REDIS_DB
            #   value: "<rtm_redis_db>"
            # - name: RTM_REDIS_PASSWORD
            #   valueFrom:
            #     secretKeyRef:
            #       name: apidog-secrets
            #       key: rtm-redis-password
            # - name: RTM_REDIS_TLS_ENABLED
            #   value: "false"
            # Application configuration
            - name: JWT_SECRET
              valueFrom:
                secretKeyRef:
                  name: apidog-secrets
                  key: jwt-secret
            - name: LICENSE
              value: "<License token>"
            - name: BASE_URL
              value: "https://apidog.example.com"
            - name: ADMIN_USERNAME
              value: "admin"
            - name: ADMIN_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: apidog-secrets
                  key: admin-password
            # Email configuration - Optional
            - name: MAILER_HOST
              value: "smtp.gmail.com"
            - name: MAILER_PORT
              value: "465"
            - name: MAILER_SECURE
              value: "true"
            - name: MAILER_USER
              value: "service@email.example.com"
            - name: MAILER_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: apidog-secrets
                  key: mailer-password
            # OAuth2 SSO configuration - Optional
            # Uncomment and configure if using OAuth2 SSO:
            # - name: OAUTH2_ENABLE
            #   value: "true"
            # - name: OAUTH2_AUTH_URL
            #   value: "https://login.microsoftonline.com/example-....-example/oauth2/v2.0/authorize"
            # - name: OAUTH2_ACCESS_TOKEN_URL
            #   value: "https://login.microsoftonline.com/example-....-example/oauth2/v2.0/token"
            # - name: OAUTH2_CLIENT_ID
            #   value: "<client_id>"
            # - name: OAUTH2_CLIENT_SECRET
            #   valueFrom:
            #     secretKeyRef:
            #       name: apidog-secrets
            #       key: oauth2-client-secret
            # - name: OAUTH2_LOGIN_TITLE
            #   value: "Continue with OIDC"
            # - name: OAUTH2_USER_INFO_URL
            #   value: "https://login.microsoftonline.com/example-....-example/openid/userinfo"
            # - name: OAUTH2_SCOPE
            #   value: "sub,email,profile,openid"
            # - name: OAUTH2_USER_ID_ATTR
            #   value: "sub"
            # Okta SSO configuration - Optional
            # Uncomment and configure if using Okta SSO:
            # - name: OKTA_ENABLE
            #   value: "true"
            # - name: OKTA_CLIENT_ID
            #   value: "<client_id>"
            # - name: OKTA_CLIENT_SECRET
            #   valueFrom:
            #     secretKeyRef:
            #       name: apidog-secrets
            #       key: okta-client-secret
            # - name: OKTA_DOMAIN
            #   value: "<domain>"
            # - name: OKTA_LOGIN_USER_ID_ATTR
            #   value: "id"
            # LDAP SSO configuration - Optional
            # Uncomment and configure if using LDAP SSO:
            # - name: LDAP_ENABLE
            #   value: "true"
            # - name: LDAP_URL
            #   value: "ldap://192.168.10.64:389"
            # - name: LDAP_BIND_USER
            #   value: "CN=Administrators,CN=Users,DC=apidog,DC=com"
            # - name: LDAP_BIND_PASSWORD
            #   valueFrom:
            #     secretKeyRef:
            #       name: apidog-secrets
            #       key: ldap-bind-password
            # - name: LDAP_BASE_DN
            #   value: "DC=apidog,DC=com"
            # - name: LDAP_LOGIN_TITLE
            #   value: "Continue with LDAP"
            # - name: LDAP_USER_ID_ATTR
            #   value: "sAMAccountName"
            # - name: LDAP_USERNAME_ATTR
            #   value: "sAMAccountName"
            # - name: LDAP_USER_EMAIL_ATTR
            #   value: "mail"
            # - name: LDAP_SEARCH_FILTER
            #   value: "(&(sAMAccountName={{username}}))"
            # Uncomment if you need employee number attribute:
            # - name: LDAP_EMPLOYEE_NUMBER_ATTR
            #   value: "uidNumber"
            # Port configuration
            - name: REPLACE_PORT_NUMBER_80
              value: "80"
            - name: REPLACE_PORT_NUMBER_443
              value: "443"
            # Storage configuration (S3 or S3-compatible)
            - name: STORAGE_DRIVER
              value: "s3"
            - name: STORAGE_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name: apidog-secrets
                  key: storage-access-key
            - name: STORAGE_ACCESS_SECRET
              valueFrom:
                secretKeyRef:
                  name: apidog-secrets
                  key: storage-access-secret
            - name: STORAGE_BUCKET
              value: "<bucket_name>"
            - name: STORAGE_BASE_URL
              value: "<storage_base_url>"
            # Uncomment if using non-AWS S3-compatible storage (e.g., MinIO):
            # - name: STORAGE_CUSTOM_ENDPOINT
            #   value: "<custom_endpoint>"
            # - name: STORAGE_BUCKET_PATH_STYLE
            #   value: "true"
            # - name: STORAGE_IS_ARN_REGION
            #   value: "false"
            # - name: STORAGE_SIGNATURE_VERSION
            #   value: "v2"
            # Optional: Custom 404 page URL
            # - name: NOT_FOUND_PAGE_URL
            #   value: "https://apidog.example.com/web/"
          volumeMounts:
            - name: logs
              mountPath: /usr/src/app/logs
            - name: data
              mountPath: /usr/src/app/app/public/static-upload
          ports:
            - containerPort: 80
          livenessProbe:
            exec:
              command:
                - "sh"
                - "-c"
                - "wget --tries=1 --spider http://127.0.0.1:5636/api/v1/ping && wget --tries=1 --spider http://127.0.0.1:3000/api/v1/ping && wget --tries=1 --spider http://127.0.0.1:80/api/v1/configs/client && wget --tries=1 --spider http://127.0.0.1:80/api/v1/ping"
            initialDelaySeconds: 60
            periodSeconds: 60
      volumes:
        - name: logs
          hostPath:
            path: /data/apidog/logs
            type: DirectoryOrCreate
        - name: data
          hostPath:
            path: /data/apidog/data
            type: DirectoryOrCreate
```

3. **Apply Deployment:**

Apply the manifest to your cluster.

```bash
kubectl apply -f deployment.yaml
```

## Post-Deployment Management

After deploying with Helm, you can use kubectl commands to view pods, manage and troubleshoot your Apidog deployment:

### Viewing Pods and Logs

```bash
# List pods in the apidog
kubectl get pods -n apidog

# Get pod logs
kubectl logs <pod-name> -n apidog

# Follow pod logs in real-time
kubectl logs -f <pod-name> -n apidog
```

### Debugging with Interactive Shell

```bash
# Get an interactive shell in the pod
kubectl exec -it <pod-name> -n apidog -- /bin/sh
```

## Verify System Health Check

Execute the following command to check the internal health status using the built-in doctor script.

```bash
kubectl exec -it <pod-name> -n apidog -- /bin/sh -c "cd /usr/src/app && ./doctor"
```

The application has started successfully if the output includes lines similar to the following:

```
[WARN] This Redis server's `default` user does not require a password, but a password was supplied
connect succeeded! value is  null
Executing (default): SELECT 1+1 AS result
...
Connecting to 127.0.0.1:3000 (127.0.0.1:3000)
remote file exists
Connecting to 127.0.0.1 (127.0.0.1:80)
remote file exists
Connecting to 127.0.0.1 (127.0.0.1:80)
remote file exists
Checking BASE_URL: https://your-base-url.com
API base URL matches expected BASE_URL
```

:::info[]
If the output differs from the above, the application has either not started successfully or is still in the process of starting. A typical graceful startup time is around 30 seconds, though this may vary depending on hardware performance.
:::

## Run the application


To run the application, refer to the documentations:

[Accessing Apidog Web Interface](https://self-hosting.apidog.com/accessing-apidog-web-interface-405307m0.md)
[Accessing Apidog Admin Panel](https://self-hosting.apidog.com/accessing-apidog-admin-panel-700382m0.md)
[Installing Apidog On-Premises Client](https://self-hosting.apidog.com/installing-apidog-on-premises-client-700348m0.md)
 

## Other Resources

[Using LDAP for Authentication](https://self-hosting.apidog.com/using-ldap-for-authentication-405303m0.md)
[Using OKTA for Authentication](https://self-hosting.apidog.com/using-okta-for-authentication-405304m0.md)
[Using OAuth2.0 for Authentication](https://self-hosting.apidog.com/using-oauth2-0-for-authentication-481407m0.md)
[Troubleshooting Guide](doc-405314)
[Configuration Guide](doc-405300)
[Updating Apidog](https://self-hosting.apidog.com/updating-apidog-405312m0.md)
[Backing up Apidog](https://self-hosting.apidog.com/backing-up-apidog-405313m0.md)
[License Renewal](https://self-hosting.apidog.com/license-renewal-703533m0.md)
