Large Language Model (LLM)

What is Dify, and How to Deploy It in an Enterprise Data Stack?

Last updated on
May 12, 2026

What is Dify?

Dify is a platform that allows users to create multi-step workflows and interact with large language models (LLMs) like GPT. Dify has a user-friendly interface, extensive customization options, and ability to integrate various APIs and models. Users can build assistants for specific domains like finance or law by configuring the appropriate tools, vector databases, and prompts within Dify's workflow editor.

Watch Dify in action

No items found.

Why is Dify better on Shakudo?

Deploying Dify on your own infrastructure can be a daunting task, requiring significant DevOps resources and expertise. You'd need to handle infrastructure provisioning, security configurations, integrations with various tools and data sources, and ongoing maintenance and updates – all while ensuring reliability, performance, and cost-effectiveness. This can quickly become a maintenance nightmare, draining your team's time and budget.

With Shakudo, you can seamlessly integrate Dify into your existing data stack within your secure VPC or on-premises environment, with just a few clicks. Our automated DevOps platform handles the entire deployment, integration, and maintenance process, allowing your team to focus on building and launching innovative AI solutions. Shakudo ensures a reliable, high-performance, and cost-optimized experience by creating compatibility across best-of-breed data tools, streamlining workflows, and automating DevOps processes. You gain the power of Dify.AI's advanced LLM application development capabilities without the operational complexities, enabling you to drive real business value from AI initiatives

Why is better on Shakudo?

Core Shakudo Features

Own Your AI

Keep data sovereign, protect IP, and avoid vendor lock-in with infra-agnostic deployments.

Faster Time-to-Value

Pre-built templates and automated DevOps accelerate time-to-value.
integrate

Flexible with Experts

Operating system and dedicated support ensure seamless adoption of the latest and greatest tools.
See Shakudo in Action
Neal Gilmore
Get Started >

Deployment Runbook


Variables

export KCFG=/path/to/customer-kubeconfig
export KCTX=<customer-kubernetes-context>
export DIFY_NS=<dify-namespace>
export VALKEY_NS=<valkey-namespace>
export RELEASE=<helm-release-name>
export VALKEY_HOST=<valkey-service-name-or-fqdn>

Safety rules

Confirm the target context

kubectl --kubeconfig "$KCFG" --context "$KCTX" config current-context

Back up before changing anything

kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" get secret dify-api -o yaml > /tmp/dify-api_backup.yaml

Do not do these by default

Phase 1 — Immediate post-deploy validation

Confirm Dify components are up

kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" get deploy,pods,svc

Confirm Redis and Valkey visibility

kubectl --kubeconfig "$KCFG" --context "$KCTX" get pods,svc,statefulset -A | egrep 'redis|valkey|dify'

Confirm Valkey health first

export VALKEY_PASSWORD="$(kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$VALKEY_NS"   get secret dify-valkey-auth -o jsonpath='{.data.password}' | base64 -d)"

kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$VALKEY_NS" exec dify-valkey-0 -- redis-cli -a "$VALKEY_PASSWORD" PING
kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$VALKEY_NS" exec dify-valkey-0 -- redis-cli -a "$VALKEY_PASSWORD" INFO keyspace

Check for mixed Redis/Valkey state in worker logs

kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS"   logs deploy/dify-worker --since=30m | egrep 'transport:|results:|Connected to redis://|valkey'

Mixed-state symptom:

Inspect active references

kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS"   get deploy,sts,configmap,secret -o yaml   | egrep -n 'dify-redis-master|dify-valkey|REDIS_HOST|CELERY_BROKER_URL|CELERY_RESULT_BACKEND'

Inspect envFrom sources

kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" get deploy dify-api -o json   | jq '{name: .metadata.name, envFrom: (.spec.template.spec.containers[0].envFrom // []), env: (.spec.template.spec.containers[0].env // [])}'

kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" get deploy dify-worker -o json   | jq '{name: .metadata.name, envFrom: (.spec.template.spec.containers[] | select(.name=="worker") | {envFrom, env})}'

Inspect the actual live broker values

for name in dify-api dify-worker; do
 echo "== configmap/$name =="
 kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" get configmap "$name" -o json     | jq -r '.data | to_entries[]
     | select(.key=="CELERY_BROKER_URL" or .key=="CELERY_RESULT_BACKEND" or .key=="REDIS_HOST")
     | [.key, (.value|gsub("//:([^@]+)@"; "//:**@"))] | @tsv'

 echo "== secret/$name =="
 kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" get secret "$name" -o json     | jq -r '.data
     | with_entries(.value |= @base64d)
     | to_entries[]
     | select(.key=="CELERY_BROKER_URL" or .key=="CELERY_RESULT_BACKEND" or .key=="REDIS_PASSWORD")
     | if (.key=="REDIS_PASSWORD") then [.key, "<redacted>"]
       else [.key, (.value|gsub("//:([^@]+)@"; "//:**@"))] end
     | @tsv'
done

If ConfigMaps already show Valkey but Secrets still show Redis for CELERY_BROKER_URL, the environment is still mixed.

Known issues this runbook handles

Redis vs Valkey mismatch

Worker transport still points to Redis while results point to Valkey.

Partial config updates

ConfigMaps look correct, but Secrets still send broker traffic to Redis.

REDIS_HOST-only fix does not work

Changing only REDIS_HOST does not complete the broker cutover.

Phase 2 — Backups before mutation

Back up Helm state

helm --kubeconfig "$KCFG" --kube-context "$KCTX" -n "$DIFY_NS" get values "$RELEASE" -a > /tmp/${RELEASE}_values_before.yaml
helm --kubeconfig "$KCFG" --kube-context "$KCTX" -n "$DIFY_NS" get manifest "$RELEASE" > /tmp/${RELEASE}_manifest_before.yaml
helm --kubeconfig "$KCFG" --kube-context "$KCTX" -n "$DIFY_NS" history "$RELEASE" > /tmp/${RELEASE}_history_before.txt

Back up live resources

for r in deployment/dify-api deployment/dify-worker deployment/dify-plugin-daemon          configmap/dify-api configmap/dify-worker configmap/dify-plugin-daemon          secret/dify-api secret/dify-worker; do
 kind="${r%%/*}"
 name="${r##*/}"
 kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" get "$kind" "$name" -o yaml > /tmp/${name}_backup.yaml
done

Phase 3 — Preferred permanent fix in source-of-truth

If your deployment package exposes the correct Helm values, the exact stale paths found in staging were:

These should point to Valkey, not Redis.

Phase 4 — Staging-proven live fix

Patch the secret-backed broker URL using the already-working Valkey result backend

for name in dify-api dify-worker; do
 broker="$(kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" get secret "$name" -o json     | jq -r '.data.CELERY_RESULT_BACKEND | @base64d')"

 kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" patch secret "$name" --type merge     -p "$(jq -nc --arg v "$broker" '{stringData:{CELERY_BROKER_URL:$v}}')"
done

Patch remaining REDIS_HOST references

for name in dify-api dify-worker dify-plugin-daemon; do
 kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" patch configmap "$name" --type merge     -p "$(jq -nc --arg host "$VALKEY_HOST" '{data:{REDIS_HOST:$host}}')"
done

Restart only affected deployments

for d in dify-api dify-worker dify-plugin-daemon; do
 kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" rollout restart deployment "$d"
 kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" rollout status deployment "$d" --timeout=300s
done

Phase 5 — Final validation block

Confirm rollouts succeeded

for d in dify-api dify-worker dify-plugin-daemon; do
 kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" rollout status deployment "$d" --timeout=300s
done

Confirm live Secret values

for name in dify-api dify-worker; do
 echo "== secret/$name =="
 kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" get secret "$name" -o json     | jq -r '.data
     | with_entries(.value |= @base64d)
     | to_entries[]
     | select(.key=="CELERY_BROKER_URL" or .key=="CELERY_RESULT_BACKEND")
     | [.key, (.value|gsub("//:([^@]+)@"; "//:**@"))] | @tsv'
done

Confirm live ConfigMap values

for name in dify-api dify-worker dify-plugin-daemon; do
 echo "== configmap/$name =="
 kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" get configmap "$name" -o json     | jq -r '.data | to_entries[]
     | select(.key=="CELERY_BROKER_URL" or .key=="CELERY_RESULT_BACKEND" or .key=="REDIS_HOST")
     | [.key, (.value|gsub("//:([^@]+)@"; "//:**@"))] | @tsv' || true
done

Confirm worker logs show full cutover

kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS"   logs deploy/dify-worker --since=30m | egrep 'transport:|results:|Connected to redis://|valkey'

Expected:

Confirm no dify-redis-master references remain

kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS"   get deploy,configmap,secret -o yaml | grep -n 'dify-redis-master' || true

Confirm Valkey is still healthy after cutover

kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$VALKEY_NS"   exec dify-valkey-0 -- redis-cli -a "$VALKEY_PASSWORD" PING

kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$VALKEY_NS"   exec dify-valkey-0 -- redis-cli -a "$VALKEY_PASSWORD" INFO keyspace

Run real functional checks

Validate:

Go-live handoff checklist

Rollback

Restore live resource backups

Use the YAML backups in /tmp.

If Helm was used, roll back Helm

helm --kubeconfig "$KCFG" --kube-context "$KCTX" -n "$DIFY_NS" rollback "$RELEASE" <previous_revision>

Re-check rollouts and logs

kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" rollout status deployment/dify-api --timeout=300s
kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" rollout status deployment/dify-worker --timeout=300s
kubectl --kubeconfig "$KCFG" --context "$KCTX" -n "$DIFY_NS" logs deploy/dify-worker --since=30m

What not to do

Prerequisites

Step 1 — Prepare deployment values

export DIFY_NAMESPACE="dify"
export DIFY_RELEASE="dify"
export DIFY_HOST="<dify-subdomain>.<customer-domain>"
export STORAGE_CLASS="<storage-class>"

kubectl create namespace "$DIFY_NAMESPACE" --dry-run=client -o yaml > namespace.yaml

Step 2 — Create required secrets

kubectl -n "$DIFY_NAMESPACE" create secret generic dify-secrets   --from-literal=DB_USERNAME="<db-user>"   --from-literal=DB_PASSWORD="<db-password>"   --from-literal=REDIS_PASSWORD="<redis-or-valkey-password>"   --from-literal=SECRET_KEY="<dify-secret-key>"   --dry-run=client -o yaml > dify-secrets.yaml

# Store and apply secrets through the approved deployment workflow.
kubectl apply -f namespace.yaml
kubectl apply -f dify-secrets.yaml

Step 3 — Configure Helm values

cat > dify-values.yaml <<'EOF'
global:
 host: <dify-subdomain>.<customer-domain>

ingress:
 enabled: true
 tls: true

postgresql:
 external: true
 host: <postgres-host>
 database: dify
 existingSecret: dify-secrets

redis:
 external: true
 host: <redis-or-valkey-host>
 existingSecret: dify-secrets

objectStorage:
 provider: s3
 bucket: <dify-bucket>
 endpoint: <object-storage-endpoint>

persistence:
 storageClass: <storage-class>

secrets:
 existingSecret: dify-secrets
EOF

Step 4 — Deploy Dify

helm upgrade --install "$DIFY_RELEASE" <dify-chart>   --namespace "$DIFY_NAMESPACE"   --values dify-values.yaml

kubectl -n "$DIFY_NAMESPACE" rollout status deploy/dify-api --timeout=300s
kubectl -n "$DIFY_NAMESPACE" rollout status deploy/dify-web --timeout=300s
kubectl -n "$DIFY_NAMESPACE" rollout status deploy/dify-worker --timeout=300s

Step 5 — Validate Kubernetes resources

kubectl -n "$DIFY_NAMESPACE" get pods
kubectl -n "$DIFY_NAMESPACE" get svc
kubectl -n "$DIFY_NAMESPACE" get ingress
kubectl -n "$DIFY_NAMESPACE" logs deploy/dify-api --tail=100
kubectl -n "$DIFY_NAMESPACE" logs deploy/dify-worker --tail=100

Step 6 — Validate platform access

# Validate from Shakudo:
# 1. Log in to Shakudo.
# 2. Open Stack Components.
# 3. Launch Dify.
# 4. Confirm the Dify UI loads through the Shakudo-managed route.
# 5. Confirm admin login and workspace access.

curl -I "https://$DIFY_HOST"

Step 7 — Smoke test Dify

# In the Dify UI:
# - Configure one approved model provider.
# - Create a test chat app.
# - Send a simple prompt.
# - Upload one small approved document to a knowledge base.
# - Ask one document-grounded question.

kubectl -n "$DIFY_NAMESPACE" logs deploy/dify-worker --tail=100

Rollback guidance

helm -n "$DIFY_NAMESPACE" history "$DIFY_RELEASE"
helm -n "$DIFY_NAMESPACE" rollback "$DIFY_RELEASE" <revision>

# For first-time failed installs, uninstall only after confirming whether PVCs,
# buckets, databases, or secrets must be retained.
helm -n "$DIFY_NAMESPACE" uninstall "$DIFY_RELEASE"

Handoff checklist