{"id":818,"date":"2024-01-23T01:04:00","date_gmt":"2024-01-23T01:04:00","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=818"},"modified":"2024-01-23T01:04:00","modified_gmt":"2024-01-23T01:04:00","slug":"setup-ingress-kubernetes-nginx-controller","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=818","title":{"rendered":"Kubernetes Gateway API: A Beginner&#8217;s Guide"},"content":{"rendered":"<p>In this comprehensive ingress guide, you will learn how to<strong> setup Nginx ingress controller <\/strong>on Kubernetes<strong> <\/strong>and configure ingress using DNS.<\/p>\n<p>If you want to understand how Kubernetes ingress works, read my <a href=\"https:\/\/devopscube.com\/kubernetes-ingress-tutorial\/\" rel=\"noreferrer noopener\">Kubernetes Ingress Tutorial.<\/a> for beginners. I have explained all the core ingress concepts including how an ingress object works with an ingress controller.<\/p>\n<p>There are two Nginx ingress controllers.<\/p>\n<ol>\n<li><a href=\"https:\/\/github.com\/kubernetes\/ingress-nginx?ref=devopscube.com\" rel=\"noreferrer noopener\">Nginx ingress controller by kubernetes community<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/nginxinc\/kubernetes-ingress?ref=devopscube.com\" rel=\"noreferrer noopener\">Nginx ingress controller by Nginx Inc<\/a><\/li>\n<\/ol>\n<p>We will be using the Kubernetes community Nginx controller.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Note<\/strong><\/b>: Today, you can get <b><strong style=\"white-space: pre-wrap;\">25% discount<\/strong><\/b> on Kubernetes CKA, CKAD, CKS, KCNA certifications using code <b><strong style=\"white-space: pre-wrap;\">DCUBE20<\/strong><\/b> at <a href=\"https:\/\/kube.promo\/latest?ref=devopscube.com\" rel=\"noreferrer noopener\">kube.promo\/latest<\/a><\/div>\n<\/div>\n<h2 id=\"ingress-nginx-ingress-controller-architecture\">Ingress &amp; Nginx Ingress Controller Architecture<\/h2>\n<p>Here is a high-level architecture of Kubernetes ingress using the Nginx ingress controller. In this guide, we will learn to build the setup in the architecture.<\/p>\n<p>(<strong>Note<\/strong>: Click the image to view in high resolution)<\/p>\n<figure class=\"kg-card kg-image-card kg-card-hascaption\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-5-43.png\" class=\"kg-image\" alt=\"Nginx ingress controller deployment architecture\" loading=\"lazy\" width=\"1027\" height=\"1255\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-5-43.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/image-5-43.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-5-43.png 1027w\" sizes=\"auto, (min-width: 720px) 720px\"><figcaption><span style=\"white-space: pre-wrap;\">Click to view in HD<\/span><\/figcaption><\/figure>\n<h2 id=\"prerequisites\">Prerequisites<\/h2>\n<ol>\n<li>A <a href=\"https:\/\/devopscube.com\/setup-kubernetes-cluster-kubeadm\/\">Kubernetes cluster<\/a><\/li>\n<li>kubectl utility installed and authenticated to kubernetes cluster.<\/li>\n<li>Admin access to kubernetes cluster.<\/li>\n<li>A valid domain to point to ingress controller Load Balancer IP. (<strong>Optional<\/strong>)<\/li>\n<\/ol>\n<p>If you are trying this setup on Google Cloud, assign admin permissions to your account to enable cluster roles.<\/p>\n<pre><code>ACCOUNT=$(gcloud info --format='value(config.account)')\nkubectl create clusterrolebinding owner-cluster-admin-binding \\\n    --clusterrole cluster-admin \\\n    --user $ACCOUNT<\/code><\/pre>\n<h2 id=\"nginx-ingress-controller-kubernetes-manifests\">Nginx Ingress Controller Kubernetes Manifests<\/h2>\n<p>All the kubernetes manifests used in this tutorial are hosted on the <a href=\"https:\/\/github.com\/techiescamp\/nginx-ingress-controller?ref=devopscube.com\" rel=\"noreferrer noopener\">Github repository<\/a>.<\/p>\n<p>Clone it and you can deploy the YAML files directly as you follow along the guide. These manifests are taken from the official Nginx community repo.<\/p>\n<pre><code>git clone https:\/\/github.com\/techiescamp\/nginx-ingress-controller<\/code><\/pre>\n<p>First, we will understand all the associated Kubernetes objects by <strong>deploying Nginx controllers using YAML manifests<\/strong>. Once we have the understanding, we will <strong>deploy it using the Helm chart<\/strong>.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Note<\/strong><\/b>: If you want to understand all the Nginx ingress controllers objects and how they relate to each other, I suggest you create objects individually from the repo. Once you know how it works, you can use a single manifest or a helm chart to deploy it.<\/div>\n<\/div>\n<p>If you want to deploy all the objects on one go, open the cloned repo in termal.<\/p>\n<p>cd in in to the manifest folder and execute the following command. It will deploy all the manifests explained in this blog.<\/p>\n<pre><code>kubectl apply -f .<\/code><\/pre>\n<h2 id=\"deploy-nginx-ingress-controller-with-manifests\">Deploy Nginx Ingress Controller With Manifests<\/h2>\n<p>We need to deploy the following <a href=\"https:\/\/devopscube.com\/kubernetes-objects-resources\/\">Kubernetes objects<\/a> to have a <strong>working Nginx controller<\/strong>.<\/p>\n<ol>\n<li><code>ingress-nginx<\/code> namespace<\/li>\n<li>Service account\/Roles\/ClusterRoles for <strong>Nginx admission controller<\/strong><\/li>\n<li>Validating webhook Configuration<\/li>\n<li>Jobs to create\/update Webhook CA bundles<\/li>\n<li>Service account\/Roles\/ClusterRoles of <strong>Nginx controller deployment<\/strong><\/li>\n<li>Nginx controller configmap<\/li>\n<li>Services for nginx controller &amp; admission controller<\/li>\n<li>Ingress controller deployment<\/li>\n<\/ol>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Note<\/strong><\/b>: You can create all the manifests yourself or use the Github repo. However, I highly suggest you go through every manifest and understand what you are deploying.<\/div>\n<\/div>\n<h3 id=\"need-for-admission-controller-validating-webhook\">Need for Admission Controller &amp; Validating Webhook<\/h3>\n<p>Kubernetes Admission Controller is a small piece of code to validate or update Kubernetes objects before creating them. In this case, it&#8217;s an <strong>admission controller to validate the ingress objects<\/strong>. The Admission Controller code is part of the Nginx controller that listens on the port <code>8443<\/code>.<\/p>\n<p>Why do need the <strong>admission controller for ingress?<\/strong><\/p>\n<p>Without an admission controller, you can deploy ingress object that <strong>might contain wrong configurations<\/strong>. A wrong configuration can break all the ingress rules associated with the ingress controller.<\/p>\n<p>With the admission controller in place, if you deploy a ingress object with wrong configurations, it will throw an error. This way you can ensure that the ingress object you create has the correct configurations and doesn&#8217;t break routing rules.<\/p>\n<p>Here is how admission controllers work for Nginx.<\/p>\n<figure class=\"kg-card kg-image-card kg-card-hascaption\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-14-36.png\" class=\"kg-image\" alt=\"nginx ingress controller validation admission controller explained.\" loading=\"lazy\" width=\"749\" height=\"583\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-14-36.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-14-36.png 749w\" sizes=\"auto, (min-width: 720px) 720px\"><figcaption><span style=\"white-space: pre-wrap;\">Click to view in HD<\/span><\/figcaption><\/figure>\n<ol>\n<li>When you deploy an ingress YAML, the Validation admission intercepts the request.<\/li>\n<li>Kubernetes API then sends the ingress object to the validation admission controller service endpoint based on admission webhook endpoints.<\/li>\n<li>Service sends the request to the Nginx deployment on port 8443 for validating the ingress object.<\/li>\n<li>The admission controller then sends a response to the k8s API.<\/li>\n<li>If it is a valid response, the API will create the ingress object.<\/li>\n<\/ol>\n<p>Now let&#8217;s get started by creating Kubernetes objects for the ingress controller.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Note<\/strong><\/b>: In the following sections you dont necessarily have to copy and create the YAML files. You can use the files from the repository directly and deploy it. I have given the full YAMLs here for reference.<\/div>\n<\/div>\n<h3 id=\"create-a-namespace\">Create a Namespace<\/h3>\n<p>We will deploy all the Nginx controller objects in the <code>ingress-nginx<\/code> namespace.<\/p>\n<p>Let&#8217;s create the namespace.<\/p>\n<pre><code>kubectl create ns ingress-nginx<\/code><\/pre>\n<h3 id=\"create-admission-controller-roles-service-account\">Create Admission Controller Roles &amp; Service Account<\/h3>\n<p>We need a Role and ClusterRole with required permissions and bind to <code>ingress-nginx-admission<\/code> service account.<\/p>\n<p>Create a file named <code>admission-service-account.yaml<\/code> and copy the following contents.<\/p>\n<pre><code>---\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  labels:\n    app.kubernetes.io\/component: admission-webhook\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx-admission\n  namespace: ingress-nginx\n\n---\napiVersion: rbac.authorization.k8s.io\/v1\nkind: Role\nmetadata:\n  annotations:\n    app.kubernetes.io\/component: admission-webhook\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx-admission\n  namespace: ingress-nginx\nrules:\n- apiGroups:\n  - \"\"\n  resources:\n  - secrets\n  verbs:\n  - get\n  - create\n\n---\napiVersion: rbac.authorization.k8s.io\/v1\nkind: RoleBinding\nmetadata:\n  labels:\n    app.kubernetes.io\/component: admission-webhook\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx-admission\n  namespace: ingress-nginx\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: Role\n  name: ingress-nginx-admission\nsubjects:\n- kind: ServiceAccount\n  name: ingress-nginx-admission\n  namespace: ingress-nginx\n\n\n---\napiVersion: rbac.authorization.k8s.io\/v1\nkind: ClusterRole\nmetadata:\n  labels:\n    app.kubernetes.io\/component: admission-webhook\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx-admission\nrules:\n- apiGroups:\n  - admissionregistration.k8s.io\n  resources:\n  - validatingwebhookconfigurations\n  verbs:\n  - get\n  - update\n\n---\napiVersion: rbac.authorization.k8s.io\/v1\nkind: ClusterRoleBinding\nmetadata:\n  labels:\n    app.kubernetes.io\/component: admission-webhook\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx-admission\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: ClusterRole\n  name: ingress-nginx-admission\nsubjects:\n- kind: ServiceAccount\n  name: ingress-nginx-admission\n  namespace: ingress-nginx<\/code><\/pre>\n<p>Deploy the manifest.<\/p>\n<pre><code>kubectl apply -f admission-service-account.yaml <\/code><\/pre>\n<h3 id=\"create-validating-webhook-configuration\">Create Validating Webhook Configuration<\/h3>\n<p>Create a file named <code>validating-webhook.yaml<\/code> and copy the following contents.<\/p>\n<pre><code>---\napiVersion: admissionregistration.k8s.io\/v1\nkind: ValidatingWebhookConfiguration\nmetadata:\n  labels:\n    app.kubernetes.io\/component: admission-webhook\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx-admission\nwebhooks:\n- admissionReviewVersions:\n  - v1\n  clientConfig:\n    service:\n      name: ingress-nginx-controller-admission\n      namespace: ingress-nginx\n      path: \/networking\/v1\/ingresses\n  failurePolicy: Fail\n  matchPolicy: Equivalent\n  name: validate.nginx.ingress.kubernetes.io\n  rules:\n  - apiGroups:\n    - networking.k8s.io\n    apiVersions:\n    - v1\n    operations:\n    - CREATE\n    - UPDATE\n    resources:\n    - ingresses\n  sideEffects: None<\/code><\/pre>\n<p>Create the <code>ValidatingWebhookConfiguration<\/code><\/p>\n<pre><code>kubectl apply -f validating-webhook.yaml<\/code><\/pre>\n<h3 id=\"deploy-jobs-to-update-webhook-certificates\">Deploy Jobs To Update Webhook Certificates<\/h3>\n<p>The  <code>ValidatingWebhookConfiguration<\/code> works only over HTTPS. So it needs a CA bundle.<\/p>\n<p>We use <a href=\"https:\/\/github.com\/jet\/kube-webhook-certgen?ref=devopscube.com\" rel=\"noreferrer noopener\">kube-webhook-certgen<\/a> to generate a CA cert bundle with the first job. The generated CA certs are stored in a secret named <code>ingress-nginx-admission<\/code><\/p>\n<p>The second job patches the <code>ValidatingWebhookConfiguration<\/code> object with the CA bundle.<\/p>\n<p>Create a file named <code>jobs.yaml<\/code> and copy the following contents.<\/p>\n<pre><code>\n---\napiVersion: batch\/v1\nkind: Job\nmetadata:\n  labels:\n    app.kubernetes.io\/component: controller\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx-admission-create\n  namespace: ingress-nginx\nspec:\n  template:\n    metadata:\n      labels:\n        app.kubernetes.io\/component: controller\n        app.kubernetes.io\/instance: ingress-nginx\n        app.kubernetes.io\/name: ingress-nginx\n      name: ingress-nginx-admission-create\n    spec:\n      containers:\n      - args:\n        - create\n        - --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc\n        - --namespace=$(POD_NAMESPACE)\n        - --secret-name=ingress-nginx-admission\n        env:\n        - name: POD_NAMESPACE\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.namespace\n        image: registry.k8s.io\/ingress-nginx\/kube-webhook-certgen:v20231011-8b53cabe0\n        imagePullPolicy: IfNotPresent\n        name: create\n        securityContext:\n          allowPrivilegeEscalation: false\n      nodeSelector:\n        kubernetes.io\/os: linux\n      restartPolicy: OnFailure\n      securityContext:\n        runAsNonRoot: true\n        runAsUser: 2000\n      serviceAccountName: ingress-nginx-admission\n---\napiVersion: batch\/v1\nkind: Job\nmetadata:\n  labels:\n    app.kubernetes.io\/component: admission-webhook\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx-admission-patch\n  namespace: ingress-nginx\nspec:\n  template:\n    metadata:\n      labels:\n        app.kubernetes.io\/component: admission-webhook\n        app.kubernetes.io\/instance: ingress-nginx\n        app.kubernetes.io\/name: ingress-nginx\n      name: ingress-nginx-admission-patch\n    spec:\n      containers:\n      - args:\n        - patch\n        - --webhook-name=ingress-nginx-admission\n        - --namespace=$(POD_NAMESPACE)\n        - --patch-mutating=false\n        - --secret-name=ingress-nginx-admission\n        - --patch-failure-policy=Fail\n        env:\n        - name: POD_NAMESPACE\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.namespace\n        image: registry.k8s.io\/ingress-nginx\/kube-webhook-certgen:v20231011-8b53cabe0\n        imagePullPolicy: IfNotPresent\n        name: patch\n        securityContext:\n          allowPrivilegeEscalation: false\n      nodeSelector:\n        kubernetes.io\/os: linux\n      restartPolicy: OnFailure\n      securityContext:\n        runAsNonRoot: true\n        runAsUser: 2000\n      serviceAccountName: ingress-nginx-admission<\/code><\/pre>\n<p>Create the jobs<\/p>\n<pre><code>kubectl apply -f jobs.yaml<\/code><\/pre>\n<p>Verify the job completion using the following command.<\/p>\n<pre><code>kubectl get jobs -n ingress-nginx<\/code><\/pre>\n<p>Once the jobs are executed, you can describe the <code>ValidatingWebhookConfigurationand<\/code>, you will see the patched bundle.<\/p>\n<pre><code>kubectl describe ValidatingWebhookConfiguration ingress-nginx-admission<\/code><\/pre>\n<figure class=\"kg-card kg-image-card kg-card-hascaption\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-142-8.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"690\" height=\"710\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-142-8.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-142-8.png 690w\"><figcaption><span style=\"white-space: pre-wrap;\">Click to view in HD<\/span><\/figcaption><\/figure>\n<h3 id=\"create-ingress-controller-roles-service-account\">Create Ingress Controller Roles &amp; Service Account<\/h3>\n<p>Create a file named <code>ingress-service-account.yaml<\/code> and copy the following contents.<\/p>\n<pre><code>---\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  labels:\n    app.kubernetes.io\/component: admission-webhook\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx\n  namespace: ingress-nginx\n\n---\napiVersion: rbac.authorization.k8s.io\/v1\nkind: Role\nmetadata:\n  labels:\n    app.kubernetes.io\/component: controller\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx\n  namespace: ingress-nginx\nrules:\n- apiGroups:\n  - \"\"\n  resources:\n  - namespaces\n  verbs:\n  - get\n- apiGroups:\n  - \"\"\n  resources:\n  - configmaps\n  - pods\n  - secrets\n  - endpoints\n  verbs:\n  - get\n  - list\n  - watch\n- apiGroups:\n  - \"\"\n  resources:\n  - services\n  verbs:\n  - get\n  - list\n  - watch\n- apiGroups:\n  - networking.k8s.io\n  resources:\n  - ingresses\n  verbs:\n  - get\n  - list\n  - watch\n- apiGroups:\n  - networking.k8s.io\n  resources:\n  - ingresses\/status\n  verbs:\n  - update\n- apiGroups:\n  - networking.k8s.io\n  resources:\n  - ingressclasses\n  verbs:\n  - get\n  - list\n  - watch\n- apiGroups:\n  - \"\"\n  resourceNames:\n  - ingress-controller-leader\n  resources:\n  - configmaps\n  verbs:\n  - get\n  - update\n- apiGroups:\n  - \"\"\n  resources:\n  - configmaps\n  verbs:\n  - create\n- apiGroups:\n  - \"\"\n  resources:\n  - events\n  verbs:\n  - create\n  - patch\n\n---\napiVersion: rbac.authorization.k8s.io\/v1\nkind: RoleBinding\nmetadata:\n  labels:\n    app.kubernetes.io\/component: controller\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx\n  namespace: ingress-nginx\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: Role\n  name: ingress-nginx\nsubjects:\n- kind: ServiceAccount\n  name: ingress-nginx\n  namespace: ingress-nginx\n\n---\napiVersion: rbac.authorization.k8s.io\/v1\nkind: ClusterRole\nmetadata:\n  labels:\n    app.kubernetes.io\/component: controller\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx\nrules:\n- apiGroups:\n  - \"\"\n  resources:\n  - configmaps\n  - endpoints\n  - nodes\n  - pods\n  - secrets\n  - namespaces\n  verbs:\n  - list\n  - watch\n- apiGroups:\n  - \"\"\n  resources:\n  - nodes\n  verbs:\n  - get\n- apiGroups:\n  - \"\"\n  resources:\n  - services\n  verbs:\n  - get\n  - list\n  - watch\n- apiGroups:\n  - networking.k8s.io\n  resources:\n  - ingresses\n  verbs:\n  - get\n  - list\n  - watch\n- apiGroups:\n  - \"\"\n  resources:\n  - events\n  verbs:\n  - create\n  - patch\n- apiGroups:\n  - networking.k8s.io\n  resources:\n  - ingresses\/status\n  verbs:\n  - update\n- apiGroups:\n  - networking.k8s.io\n  resources:\n  - ingressclasses\n  verbs:\n  - get\n  - list\n  - watch\n\n---\napiVersion: rbac.authorization.k8s.io\/v1\nkind: ClusterRoleBinding\nmetadata:\n  labels:\n    app.kubernetes.io\/component: controller\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: ClusterRole\n  name: ingress-nginx\nsubjects:\n- kind: ServiceAccount\n  name: ingress-nginx\n  namespace: ingress-nginx<\/code><\/pre>\n<p>Deploy the manifest.<\/p>\n<pre><code> kubectl apply -f ingress-service-account.yaml<\/code><\/pre>\n<h3 id=\"create-configmap\">Create Configmap<\/h3>\n<p>With this configmap, you can <strong>customize the Nginx settings<\/strong>. For example, you can set custom headers and most of the Nginx settings.<\/p>\n<p>Please refer to the <a href=\"https:\/\/kubernetes.github.io\/ingress-nginx\/user-guide\/nginx-configuration\/configmap\/?ref=devopscube.com\" rel=\"noreferrer noopener nofollow\">official community documentation<\/a> for all the supported configurations.<\/p>\n<p>Create a file named <code>configmap.yaml <\/code>and copy the following contents.<\/p>\n<pre><code>---\napiVersion: v1\ndata:\n  allow-snippet-annotations: \"true\"\nkind: ConfigMap\nmetadata:\n  labels:\n    app.kubernetes.io\/component: controller\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx-controller\n  namespace: ingress-nginx<\/code><\/pre>\n<p>Create the configmap.<\/p>\n<pre><code>kubectl apply -f configmap.yaml<\/code><\/pre>\n<h3 id=\"create-ingress-controller-admission-controller-services\">Create Ingress Controller &amp; Admission Controller Services<\/h3>\n<p>Create a file named <code>services.yaml<\/code> and copy the following contents.<\/p>\n<pre><code>---\napiVersion: v1\nkind: Service\nmetadata:\n  labels:\n    app.kubernetes.io\/component: controller\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx-controller\n  namespace: ingress-nginx\nspec:\n  externalTrafficPolicy: Local\n  ipFamilies:\n  - IPv4\n  ipFamilyPolicy: SingleStack\n  ports:\n  - appProtocol: http\n    name: http\n    port: 80\n    protocol: TCP\n    targetPort: http\n  - appProtocol: https\n    name: https\n    port: 443\n    protocol: TCP\n    targetPort: https\n  selector:\n    app.kubernetes.io\/component: controller\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  type: LoadBalancer\n---\napiVersion: v1\nkind: Service\nmetadata:\n  labels:\n    app.kubernetes.io\/component: controller\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx-controller-admission\n  namespace: ingress-nginx\nspec:\n  ports:\n  - appProtocol: https\n    name: https-webhook\n    port: 443\n    targetPort: webhook\n  selector:\n    app.kubernetes.io\/component: controller\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  type: ClusterIP<\/code><\/pre>\n<p>Create the services.<\/p>\n<pre><code>kubectl apply -f services.yaml<\/code><\/pre>\n<p><code>ingress-nginx-controller<\/code> service <strong>creates a Loadbalancer<\/strong> in the respective cloud platform you are deploying.<\/p>\n<p>You can get the load balancer IP\/DNS using the following command.<\/p>\n<pre><code>kubectl --namespace ingress-nginx get services -o wide -w ingress-nginx-controller<\/code><\/pre>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Note<\/strong><\/b>: For each cloud provider there are specific annotations you can use to map static IP address and other configs to the Loadbalancer. Check out <a href=\"https:\/\/cloud.google.com\/kubernetes-engine\/docs\/how-to\/load-balance-ingress?ref=devopscube.com#ingress_annotations\" rel=\"noreferrer noopener\">GCP annotations here<\/a> and <a href=\"https:\/\/docs.aws.amazon.com\/eks\/latest\/userguide\/network-load-balancing.html?ref=devopscube.com\" rel=\"noreferrer noopener\">AWS annoatations here<\/a>.<\/div>\n<\/div>\n<h3 id=\"create-ingressclass\">Create IngressClass<\/h3>\n<p>Create a file named <strong><code>ingressclass.yaml<\/code><\/strong> and copy the following contents.<\/p>\n<pre><code>---\napiVersion: networking.k8s.io\/v1\nkind: IngressClass\nmetadata:\n  labels:\n    app.kubernetes.io\/component: admission-webhook\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: nginx\nspec:\n  controller: k8s.io\/ingress-nginx<\/code><\/pre>\n<p>Deploy the ingress class.<\/p>\n<pre><code>kubectl apply -f ingressclass.yaml<\/code><\/pre>\n<p>It will create a ingress class named nginx. We have to use this ingress class name in Ingress objects we create.<\/p>\n<h3 id=\"create-ingress-controller-deployment\">Create Ingress Controller Deployment<\/h3>\n<p>Create a file named <code>deployment.yaml<\/code> and copy the following contents.<\/p>\n<pre><code>---\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  labels:\n    app.kubernetes.io\/component: controller\n    app.kubernetes.io\/instance: ingress-nginx\n    app.kubernetes.io\/name: ingress-nginx\n  name: ingress-nginx-controller\n  namespace: ingress-nginx\nspec:\n  minReadySeconds: 0\n  revisionHistoryLimit: 10\n  selector:\n    matchLabels:\n      app.kubernetes.io\/component: controller\n      app.kubernetes.io\/instance: ingress-nginx\n      app.kubernetes.io\/name: ingress-nginx\n  template:\n    metadata:\n      labels:\n        app.kubernetes.io\/component: controller\n        app.kubernetes.io\/instance: ingress-nginx\n        app.kubernetes.io\/name: ingress-nginx\n    spec:\n      containers:\n      - args:\n        - \/nginx-ingress-controller\n        - --publish-service=$(POD_NAMESPACE)\/ingress-nginx-controller\n        - --election-id=ingress-controller-leader\n        - --controller-class=k8s.io\/ingress-nginx\n        - --configmap=$(POD_NAMESPACE)\/ingress-nginx-controller\n        - --validating-webhook=:8443\n        - --validating-webhook-certificate=\/usr\/local\/certificates\/cert\n        - --validating-webhook-key=\/usr\/local\/certificates\/key\n        env:\n        - name: POD_NAME\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.name\n        - name: POD_NAMESPACE\n          valueFrom:\n            fieldRef:\n              fieldPath: metadata.namespace\n        - name: LD_PRELOAD\n          value: \/usr\/local\/lib\/libmimalloc.so\n        image: registry.k8s.io\/ingress-nginx\/controller:v1.9.5\n        imagePullPolicy: IfNotPresent\n        lifecycle:\n          preStop:\n            exec:\n              command:\n              - \/wait-shutdown\n        livenessProbe:\n          failureThreshold: 5\n          httpGet:\n            path: \/healthz\n            port: 10254\n            scheme: HTTP\n          initialDelaySeconds: 10\n          periodSeconds: 10\n          successThreshold: 1\n          timeoutSeconds: 1\n        name: controller\n        ports:\n        - containerPort: 80\n          name: http\n          protocol: TCP\n        - containerPort: 443\n          name: https\n          protocol: TCP\n        - containerPort: 8443\n          name: webhook\n          protocol: TCP\n        readinessProbe:\n          failureThreshold: 3\n          httpGet:\n            path: \/healthz\n            port: 10254\n            scheme: HTTP\n          initialDelaySeconds: 10\n          periodSeconds: 10\n          successThreshold: 1\n          timeoutSeconds: 1\n        resources:\n          requests:\n            cpu: 100m\n            memory: 90Mi\n        securityContext:\n          allowPrivilegeEscalation: true\n          capabilities:\n            add:\n            - NET_BIND_SERVICE\n            drop:\n            - ALL\n          runAsUser: 101\n        volumeMounts:\n        - mountPath: \/usr\/local\/certificates\/\n          name: webhook-cert\n          readOnly: true\n      dnsPolicy: ClusterFirst\n      nodeSelector:\n        kubernetes.io\/os: linux\n      serviceAccountName: ingress-nginx\n      terminationGracePeriodSeconds: 300\n      volumes:\n      - name: webhook-cert\n        secret:\n          secretName: ingress-nginx-admission<\/code><\/pre>\n<p>Create the deployment.<\/p>\n<pre><code>kubectl apply -f deployment.yaml<\/code><\/pre>\n<p>To ensure that deployment is working, check the pod status.<\/p>\n<pre><code>kubectl get pods -n ingress-nginx<\/code><\/pre>\n<h2 id=\"validate-ingress-controller-deployment\">Validate Ingress Controller Deployment<\/h2>\n<p>You can validate the ingress controller deployment using the LoadBlancer endpoint created by the service.<\/p>\n<p>Nginx Ingress controller has a default backend. All the requests that doesn&#8217;t have a entry in the ingress goes to this default backend.<\/p>\n<p>We will validate out controller using the default backend.<\/p>\n<p>Get your Loadbalancer endpoint the try to acess it. You should get a 404 error as shown below.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-143-8.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"541\" height=\"252\"><\/figure>\n<p>Now try to access the \/heathz url using curl as given below. You should get a 200 response. Replace &lt;LOAD-BALANCER-ENDPOINT&gt; with your Loadbalancer endpoint.<\/p>\n<pre><code>curl http:\/\/&lt;LOAD-BALANCER-ENDPOINT&gt;\/healthz<\/code><\/pre>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-144-9.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"565\" height=\"250\"><\/figure>\n<h2 id=\"nginx-ingress-controller-helm-deployment\">Nginx Ingress Controller Helm Deployment<\/h2>\n<p>If you are a Helm user, you can deploy the ingress controller using the community helm chart. <code>ValidatingWebhookConfiguration<\/code> is disabled by default in <code>values.yaml<\/code>.<\/p>\n<p>Deploy the helm chart. It will create the namespace <code>ingress-nginx<\/code> if not present.<\/p>\n<pre><code>helm upgrade --install ingress-nginx ingress-nginx \\\n  --repo https:\/\/kubernetes.github.io\/ingress-nginx \\\n  --namespace ingress-nginx --create-namespace<\/code><\/pre>\n<p>Verify the helm release.<\/p>\n<pre><code>helm list -n ingress-nginx<\/code><\/pre>\n<p>To clean up the resources, uninstall the release.<\/p>\n<pre><code> helm uninstall ingress-nginx -n ingress-nginx<\/code><\/pre>\n<h2 id=\"map-a-domain-name-to-nginx-ingress-loadbalancer-ip\">Map a Domain Name To Nginx Ingress Loadbalancer IP<\/h2>\n<p>The primary goal of Ingress is to <strong>receive external traffic to services running on Kubernetes<\/strong>. Ideally in projects, a DNS would be mapped to the ingress controller Loadbalancer IP.<\/p>\n<p>This can be done via the <strong>respective DNS provider with the domain name you own<\/strong>.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-grey\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Info<\/strong><\/b>: For internet-facing apps, it will be public DNS pointing to the public IP of the load balancer. If it&#8217;s an internal app, it will be an organization&#8217;s private DNS mapped to a private load balancer IP.<\/div>\n<\/div>\n<h3 id=\"single-dns-mapping\">Single DNS Mapping<\/h3>\n<p>You can map a single domain directly as an<strong> A record to the load balancer IP<\/strong>. Using this you can have only one domain for the ingress controller and multiple path-based traffic routing.<\/p>\n<p>For example,<\/p>\n<pre><code>www.example.com --&gt; Loadbalancer IP<\/code><\/pre>\n<p>You can also have path-based routing using this model.<\/p>\n<p>Few examples,<\/p>\n<pre><code>http:\/\/www.example.com\/app1\nhttp:\/\/www.example.com\/app2\nhttp:\/\/www.example.com\/app1\/api\nhttp:\/\/www.example.com\/app2\/api<\/code><\/pre>\n<h3 id=\"wildcard-dns-mapping\">Wildcard DNS Mapping<\/h3>\n<p>If you <strong>map a wildcard DNS to the load balancer<\/strong>, you can have dynamic DNS endpoints through ingress.<\/p>\n<p>Once you add the wildcard entry in the DNS records, you need to mention the required DNS in the ingress object and the Nginx ingress controller will take care of routing it to the required service endpoint.<\/p>\n<p>For example, check the following two mappings.<\/p>\n<pre><code>*.example.com --&gt; Loadbalancer IP\n*.apps.example.com --&gt; Loadbalancer IP <\/code><\/pre>\n<p>This way you can have multiple <strong>dynamic subdomains through a single ingress controller<\/strong> and each DNS can have its own path-based routing.<\/p>\n<p>Few examples,<\/p>\n<pre><code>#URL one\n\nhttp:\/\/demo1.example.com\/api\nhttp:\/\/demo1.example.com\/api\/v1\nhttp:\/\/demo1.example.com\/api\/v2\n\n#app specific urls\n\nhttp:\/\/grafana.apps.example.com\nhttp:\/\/prometheus.apps.example.com\n\n#URL two\n\nhttp:\/\/demo2.apps.example.com\/api\nhttp:\/\/demo2.apps.example.com\/api\/v1\nhttp:\/\/demo2.apps.example.com\/api\/v2\n<\/code><\/pre>\n<p>For demo purposes, I have <strong>mapped a wildcard DNS to the LoadBalancer IP<\/strong>. Based on your DNS provider, you can add the DNS record.<\/p>\n<p>The following image shows the DNS records I used for this blog demo. <strong>I used EKS<\/strong> so instead of Loadnbalacer IP, I have a <strong>DNS of network load balancer endpoint which will be a CNAME<\/strong>. In the case of GKE, you will get an IP and in that case, you need to create an A record.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-276.png\" class=\"kg-image\" alt=\"nginx ingress controller DNS mapping\" loading=\"lazy\" width=\"765\" height=\"302\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-276.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-276.png 765w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<h2 id=\"deploy-a-demo-application\">Deploy a Demo Application<\/h2>\n<p>For testing ingress, we will deploy a demo application and add a ClusterIp service to it. This application will be accessible only within the cluster without ingress.<\/p>\n<p><strong>Step 1: <\/strong>Create a namespace named dev<\/p>\n<pre><code>kubectl create namespace dev<\/code><\/pre>\n<p><strong>Step 2:<\/strong> create a file named hello-app.yaml and copy the following contents.<\/p>\n<pre><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: hello-app\n  namespace: dev\nspec:\n  selector:\n    matchLabels:\n      app: hello\n  replicas: 2\n  template:\n    metadata:\n      labels:\n        app: hello\n    spec:\n      containers:\n      - name: hello\n        image: \"gcr.io\/google-samples\/hello-app:2.0\"<\/code><\/pre>\n<p><strong>Step 3:<\/strong> Create the deployment using kubectl<\/p>\n<pre><code>kubectl create -f hello-app.yaml<\/code><\/pre>\n<p>Check the deployment status.<\/p>\n<pre><code>kubectl get deployments -n dev<\/code><\/pre>\n<p><strong>Step 5:<\/strong> Create a file named <code>hello-app-service.yaml<\/code> and copy the following contents and save the file.<\/p>\n<pre><code>apiVersion: v1\nkind: Service\nmetadata:\n  name: hello-service\n  namespace: dev\n  labels:\n    app: hello\nspec:\n  type: ClusterIP\n  selector:\n    app: hello\n  ports:\n  - port: 80\n    targetPort: 8080\n    protocol: TCP<\/code><\/pre>\n<p><strong>Step 6:<\/strong> Create the service using kubectl.<\/p>\n<pre><code>kubectl create -f hello-app-service.yaml<\/code><\/pre>\n<h2 id=\"create-ingress-object-for-application\">Create Ingress Object for Application<\/h2>\n<p>Now let\u2019s create an ingress object to <strong>access our hello app using a DNS<\/strong>. An ingress object is nothing but a set of routing rules.<\/p>\n<p>If you are wondering how the<strong> ingress object is connected to the Nginx controller,<\/strong> the ingress controller pod connects to the <strong>Ingress API to check for rules<\/strong> and it updates its <code>nginx.conf<\/code> accordingly.<\/p>\n<p>Since I have wildcard DNS mapped (<code>*.apps.mlopshub.com<\/code>) with the DNS provider, I will use <code>demo.apps.mlopshub.com<\/code> to point to the hello app service.<\/p>\n<p><strong>Step 1:<\/strong> Create a file named <code>ingress.yaml<\/code><\/p>\n<p><strong>Step 2:<\/strong> Copy the following contents and save the file.<\/p>\n<p>Replace <code>demo.apps.mlopshub.com<\/code> with your domain name. Also, we are creating this ingress object in the <code>dev<\/code> namespace becuase the hello app is running in the <code>dev<\/code> namespace.<\/p>\n<pre><code>apiVersion: networking.k8s.io\/v1\nkind: Ingress\nmetadata:\n  name: test-ingress\n  namespace: dev\nspec:\n  ingressClassName: nginx\n  rules:\n  - host: \"demo.apps.mlopshub.com\"\n    http:\n      paths:\n        - pathType: Prefix\n          path: \"\/\"\n          backend:\n            service:\n              name: hello-service\n              port:\n                number: 80<\/code><\/pre>\n<p><strong>Step 3:<\/strong> Describe created ingress object created to check the configurations.<\/p>\n<pre><code>kubectl describe ingress  -n dev<\/code><\/pre>\n<p>Now if I try to access <code>demo.apps.mlopshub.com <\/code> domain, I will be able to access the hello app as shown below. (You should replace it with your domain name)<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-1-36.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"479\" height=\"193\"><\/figure>\n<p>You might face https error in the browser. in that case you can use a curl command to verify ingress endpoint.<\/p>\n<pre><code>curl demo.apps.mlopshub.com<\/code><\/pre>\n<h2 id=\"tls-with-nginx-ingress\">TLS With Nginx Ingress<\/h2>\n<p>You can configure TLS certificates with each ingress object. The TLS gets terminated at the ingress controller level.<\/p>\n<p>The following image shows the ingress TLS config. The TLS certificate needs to be added as a secret object.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-2-34.png\" class=\"kg-image\" alt=\"Nginx controller SSL\/TLS certificates \" loading=\"lazy\" width=\"670\" height=\"512\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/image-2-34.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-2-34.png 670w\"><\/figure>\n<p>I have written a detailed article on Ingress TLS configuration.<\/p>\n<p>\ud83d\udc49 Take a look at the <a href=\"https:\/\/devopscube.com\/configure-ingress-tls-kubernetes\/\" rel=\"noreferrer noopener\">guide to configure ingress TLS on Kubernetes.<\/a><\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>In this article, we have learned how to set up the Nginx ingress controller.<\/p>\n<p>It is very easy to get started. However, for project implementation ensure that you go through all Nginx configurations and <strong>tune them according to the requirements<\/strong>.<\/p>\n<p>With the Nginx controller configmap, you can configure all the Nginx settings without redeploying the controller.<\/p>\n<p>I hope you enjoyed this <strong>guide on Nginx ingress controller<\/strong>.<\/p>\n<p>Let me know your thoughts and queries in the comment section.<\/p>\n<p>Also, if you are learning Kubernetes, check out my  <a href=\"https:\/\/devopscube.com\/kubernetes-tutorials-beginners\/\" rel=\"noreferrer noopener\">comprehensive Kubernetes tutorials<\/a>.<\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/setup-ingress-kubernetes-nginx-controller\/\" target=\"_blank\" rel=\"noopener noreferrer\">Kubernetes Gateway API: A Beginner&#x27;s Guide \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/setup-ingress-kubernetes-nginx-controller\/<\/p>\n","protected":false},"author":1,"featured_media":819,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-818","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/818","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=818"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/818\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/819"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=818"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=818"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=818"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}