Back to Blog
    Kubernetes
    DevOps
    Production
    Best Practices
    Homelab

    The Minimalist’s Guide to Homelab Setup

    August 11, 2025
    15 min read

    The Minimalist’s Guide to Homelab Setup: Essential Apps in Just a Few Clicks with Ankra

    Welcome wanderer!

    If you’ve clicked on this blog, you either stumbled upon Ankra and are finding out how it can accelerate your productivity, or are just curious about building homelab in simple steps.

    By the end of this guide, you will have a WordPress blog hosted on your homelab, accessible securely from anywhere via a Cloudflare tunnel.

    Before we dive right into it, here are the recommended prerequisites to go ahead. In case you don’t meet the requirements, don’t worry, we got you covered.

    1. Ankra account: head out to ankra.ai to create one if you already don’t have one
    2. A Kubernetes Cluster: It does not have to be on a cloud provider. You can have a small single node cluster with kind or minikube.
    3. A domain of your choice: This is so that all your applications can have a subdomain of your choice. A domain on cloudflare or godaddy can help you access your homelab applications from anywhere in the world. If not, you can have a domain locally in your home network.

    Setting up your cluster

    Now that we have out prerequisites covered, let’s start with setting everything up.

    Upon creating an Ankra account, you shall be prompted to import a Kubernetes cluster, follow along.

    Landing Page

    Once you give it a name and click on import, you get a pop up to install Ankra agent on your cluster.

    Helm command

    If you forgot to copy the command, you can always go to settings and click on generate command like below.

    Helm command

    You should see this if the command has run successfully

    Reconciled successfully

    Now that we have connected our cluster, it’s time to deploy some stacks.

    To begin with, we need to connect a github account/repository so that a backend IAAC can be configured. Click on stacks on the left pane

    Setting up IAAC

    Default Stack Page

    Connect to GitHub

    Once this is done, any applications you add/update/delete on the UI will reflect on your repository and vice-versa.

    It’s important to know that all kubernetes manifests will automatically be committed to the git repository you connect Ankra with. Make sure you do not commit any secrets/api keys to a public repository.

    I have a cloudflare domain shivu.io and I want to deploy a wordpress app on the homelab. Wordpress can be used for various things. I want to have a handy workspace for all my blog ideas and it’s a great tool for that purpose.

    Once the stack is deployed, I will have a wordpress.shivu.io deployed with ssl enabled, so the website will be https and I can access it from anywhere on the internet.

    Deploying your stack

    A stack on Ankra is a set of applications logically put together in the form of a interactive flowchart. Ankra supports applications as Addons (helm charts) or Manifests. For my stack, I will have the following applications.

    • Wordpress: Community helm chart as an Addon
    • Cert Manager: Community helm chart as an Addon for certificates
      • ClusterIssuer: A single manifest additional to the add on to specify cloudflare as my domain issuer.
    • Cloudflared: Community helm chart as an Addon for tunnelling locally hosted applications to the internet.

    Ankra Stack

    The values.yaml for all these are as below

    wordpress-values.yaml
    1
    2# Global parameters
    3global:
    4  imageRegistry: ""
    5  imagePullSecrets: []
    6  defaultStorageClass: "standard"
    7  security:
    8    allowInsecureImages: true
    9
    10# Common configuration
    11kubeVersion: ""
    12nameOverride: ""
    13fullnameOverride: ""
    14commonLabels: {}
    15commonAnnotations: {}
    16clusterDomain: cluster.local
    17extraDeploy: []
    18usePasswordFiles: true
    19diagnosticMode:
    20  enabled: false
    21
    22# WordPress Image
    23image:
    24  registry: docker.io
    25  repository: bitnami/wordpress
    26  tag: latest
    27  digest: ""
    28  pullPolicy: IfNotPresent
    29  pullSecrets: []
    30  debug: true
    31
    32# WordPress Configuration
    33wordpressUsername: admin
    34wordpressPassword: devpassword123
    35wordpressEmail: [email protected]
    36wordpressBlogName: Dev Blog
    37persistence:
    38  enabled: true
    39  storageClass: ""
    40  accessModes:
    41    - ReadWriteOnce
    42  size: 2Gi
    43
    44# Deployment Settings
    45replicaCount: 1
    46updateStrategy:
    47  type: RollingUpdate
    48schedulerName: ""
    49terminationGracePeriodSeconds: ""
    50topologySpreadConstraints: []
    51priorityClassName: ""
    52automountServiceAccountToken: false
    53
    54# Resource Allocation
    55resourcesPreset: "nano"
    56resources:
    57  requests:
    58    cpu: 100m
    59    memory: 128Mi
    60  limits:
    61    cpu: 250m
    62    memory: 256Mi
    63
    64# Service Configuration
    65service:
    66	metadata:
    67		name: wordpress
    68  type: ClusterIP
    69  ports:
    70    http: 80
    71    https: 443
    72  clusterIP: ""
    73  loadBalancerIP: ""
    74  loadBalancerSourceRanges: []
    75
    76# Database Configuration
    77mariadb:
    78  enabled: true
    79  architecture: standalone
    80  auth:
    81    rootPassword: rootpassword
    82    database: bitnami_wordpress
    83    username: bn_wordpress
    84    password: dbpassword
    85  primary:
    86    persistence:
    87      enabled: true
    88      storageClass: ""
    89      accessModes:
    90        - ReadWriteOnce
    91      size: 2Gi
    92
    93# Disable Production Features
    94autoscaling:
    95  enabled: false
    96pdb:
    97  create: false
    98metrics:
    99  enabled: false
    100networkPolicy:
    101  enabled: false
    102memcached:
    103  enabled: false
    104
    105# Development-Specific Overrides
    106allowEmptyPassword: true
    107wordpressSkipInstall: false
    108overrideDatabaseSettings: true

    To ensure WordPress runs smoothly without crashing or timing out, it’s crucial to configure resource limits and requests in your Helm chart values. For example, setting modest CPU and memory limits helps Kubernetes manage pod stability and readiness checks effectively.

    One additional thing that cert-manager requires is the cloudflare api token to create certs for the domains configured.

    You can create this by executing:

    1kubectl -n cert-manager create secret generic cloudflare-api-token-secret --from-literal=api-token=<YOUR_CLOUDFLARE_API_TOKEN>
    cert-manager-values.yaml
    1installCRDs: true
    clusterissuer-manifest.yaml
    1
    2apiVersion: cert-manager.io/v1
    3kind: ClusterIssuer
    4metadata:
    5  name: letsencrypt-dns01
    6spec:
    7  acme:
    8    email: <your-email>
    9    privateKeySecretRef:
    10      name: letsencrypt-dns01
    11    server: https://acme-v02.api.letsencrypt.org/directory
    12    solvers:
    13    - dns01:
    14        cloudflare:
    15          apiTokenSecretRef:
    16            key: api-token
    17            name: cloudflare-api-token

    In order to configure cloudflared tunnel, you need a couple of things. In order to generate them. Install cloudflared locally. Setting this up might take a while if you are new to cloudflare, but the docs are clear and concise.

    1. Pem certificate: Upon login, a pem file is generated.
    2. Config File: Upon creation of a tunnel, a config is placed in ~/.cloudflared/config
    cloudflared-values.yaml
    1tunnelSecrets:
    2  base64EncodedPemFile: "<base64 encoded pem file>"
    3  base64EncodedConfigJsonFile: "<base64 encoded config.json"
    4
    5replica:
    6  # -- This will use DaemonSet to deploy cloudflared to all nodes
    7  allNodes: false
    8  # -- If previous flag disabled, this will use Deployment to deploy cloudflared only number of following count
    9  count: 1
    10
    11tunnelConfig:
    12  name: cloudflared-tunnel
    13  metricsUpdateFrequency: 10s
    14  logLevel: info
    15  warpRouting: true
    16
    17
    18ingress:
    19  - hostname: wordpress.shivu.io
    20    service: http://wordpress.wordpress.svc.cluster.local:80
    21  - service: http_status:404

    Click on apply on the bottom of the page and you’re good to go.

    In order to access your wordpress on the browser, you must add the URL of your tunnel as a CNAME record on the cloudflare dashboard.

    Cloudflare Dashboard

    The target here is <tunnel-id>.cfargotunnel.com. The tunnel-id can be fetched from the cloudflare dashboard or the first line of the cloudflared logs.

    Cloudflared Logs

    A quick clarification: The Cloudflared tunnel works independently from Kubernetes ingress controllers. While Kubernetes ingress manages traffic routing inside your cluster, the Cloudflared tunnel securely connects traffic from Cloudflare’s network to your cluster. Think of Cloudflared as the gateway that tunnels external requests safely, whereas Kubernetes ingress handles traffic once inside the cluster.

    Accessing your application

    If everything goes well, you should see this on your hostname

    Wordpress Screenshot

    Common points to debug:

    1. Check the logs of cert-manager if it has created the necessary certificates
    2. Check the logs of cloudflared pods if it has registered the tunnel
    3. Check the wordpress logs if it is serving traffic
    4. Check the initial cloudflared values.yaml to make sure you’ve provided the right names for the service, hostname and namespace.

    Join our slack: Join Community

    Follow us on: LinkedIn, GitHub

    Contact us: [email protected]

    Related Posts

    Master the essential strategies and patterns for running reliable, scalable applications on Kubernetes in production. Learn from real-world examples and avoid common pitfalls.

    15 min read • 8/6/2025

    A practical guide to wiring an infrastructure agent into your CI: review comments on pull requests, deploy verification on merge, and Slack reports that contain an actual root cause instead of a red X.

    12 min read • 6/10/2026