From First App to Production Platform: Databases, HTTPS, and Autoscaling on ITSH
From First App to Production Platform
Your first container is running on Kubernetes. A Deployment, a Service, an HTTPRoute, done. But a production-ready application needs more: a database, HTTPS with automatic certificates, persistent storage, and the ability to scale under load.
On a self-managed cluster, that means days of configuration. On an ITSH namespace, it’s a few YAML files.
This article covers what the platform offers beyond basic container deployment, and why you don’t need a cluster admin for any of it.
Automatic HTTPS with Let’s Encrypt
On a self-managed cluster, the path to HTTPS looks like this: install cert-manager, configure a ClusterIssuer, adjust the ingress controller, debug annotations. On ITSH, cert-manager is already installed and preconfigured. You add one annotation to your HTTPRoute to opt in to automatic TLS:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: myapp
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
parentRefs:
- name: default
namespace: nginx-gateway
sectionName: https-myapp-example-com
hostnames:
- "myapp.example.com"
rules:
- backendRefs:
- name: myapp
port: 80
The TLS certificate is provisioned automatically. No manual intervention, no renewal monitoring.
The annotation is purely an opt-in: the controller that creates the gateway listeners only checks whether it is present. Its value does not select an issuer. Which ClusterIssuer signs your certificates is determined by the annotation on the Gateway itself.
HTTP → HTTPS Redirect
To automatically redirect HTTP requests to HTTPS, add a second HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: myapp-redirect
spec:
parentRefs:
- name: default
namespace: nginx-gateway
sectionName: http
hostnames:
- "myapp.example.com"
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301
backendRefs: []
DNS Setup
Point your domain to the cluster ingress:
- A record:
91.98.6.3 - AAAA record:
2a01:4f8:1c1f:7bfa::1
The gateway sets X-Forwarded-For and X-Real-IP with the original client IP. Configure your application to accept those headers only from the gateway address shown in your customer portal. If it accepts them from any sender, clients can spoof their own IP.
Two YAML files, one DNS entry, and your app is reachable via HTTPS with automatic certificate renewal.
Managed Databases
PostgreSQL with CloudNativePG
CloudNativePG runs on the cluster and manages PostgreSQL instances as a Kubernetes resource. Creating a database:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: myapp-db
spec:
instances: 1
storage:
size: 10Gi
storageClass: hcloud-volumes
postgresql:
parameters:
max_connections: "100"
The operator automatically creates a secret called myapp-db-app containing the connection string. Reference it directly in your Deployment:
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: myapp-db-app
key: uri
10 lines of YAML for a working PostgreSQL instance with persistent storage. On your own cluster, you’d need to install the operator, configure storage classes, and define backup policies.
The example is a starting point, not a production-ready database: a single instance with no backup configuration. For production, raise instances to at least 3 and configure a backup target (an S3 bucket, for instance) along with a retention policy.
Redis-Compatible Cache with Dragonfly
For caching or session storage:
apiVersion: dragonflydb.io/v1alpha1
kind: Dragonfly
metadata:
name: myapp-cache
spec:
replicas: 1
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
Reachable at redis://myapp-cache:6379. Dragonfly speaks the Redis wire protocol, so most existing Redis clients work unchanged. There are documented behavioral differences in individual commands, so test your application before migrating.
MariaDB is also available as a database option if your application requires MySQL compatibility.
Persistent Storage and Object Storage
Block Storage for Databases and Single-Pod Apps
For workloads that need fast, dedicated storage:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myapp-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: hcloud-volumes
resources:
requests:
storage: 20Gi
hcloud-volumes provides SSD-backed block storage from Hetzner Cloud. Ideal for databases and single-pod applications. The storage class is available in paid namespaces only. On the free tier, use nfs-rwx. Volumes can also scale automatically when usage grows. For multi-pod scenarios with a shared filesystem, NFS storage (ReadWriteMany) is also available.
S3 Object Storage
For S3-compatible object storage, create a secret with your credentials and reference it in your deployment:
env:
- name: S3_ENDPOINT
valueFrom:
secretKeyRef:
name: s3-credentials
key: endpoint
- name: S3_BUCKET
valueFrom:
secretKeyRef:
name: s3-credentials
key: bucket
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: s3-credentials
key: access_key
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: s3-credentials
key: secret_key
Works with any S3-compatible SDK: AWS SDK, MinIO client, Boto3. Ideal for uploads, media files, and backups.
Autoscaling
Horizontal Pod Autoscaler
Want your application to scale up automatically during traffic spikes and scale back down after? An HPA is all it takes:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
At 70% CPU utilization, Kubernetes automatically scales up, to a maximum of 10 replicas. When load drops, it scales back down.
For advanced scenarios (scaling based on queue length, HTTP requests, or Prometheus metrics), KEDA (Kubernetes Event-Driven Autoscaling) is available.
Storage Autoscaling
Block storage volumes can also scale automatically. If your database grows beyond its initial size, you don’t have to manually resize, just add three annotations to your PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myapp-data
annotations:
resize.topolvm.io/threshold: "20%"
resize.topolvm.io/increase: "5Gi"
resize.topolvm.io/storage_limit: "50Gi"
spec:
storageClassName: hcloud-volumes
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
threshold is a free space threshold, not a utilization threshold. Expansion kicks in once less than 20% is free, meaning the volume is more than 80% full. It then grows by 5 GiB, up to the limit you set. Expansion is online, no pod restart needed. This works for standalone PVCs and database volumes (CloudNativePG, MariaDB).
Don’t mistake the value for a fill level: "80%" would mean expanding as soon as the volume is 20% full. It would then grow step by step to the limit, and you pay for that storage.
Storage is billed at the standard rate (minimum 10 GiB per volume). Volumes can only grow, never shrink. Like block storage itself, autoresize requires hcloud-volumes and is therefore available in paid namespaces only.
Resource Recommendations with the Vertical Pod Autoscaler
Not sure how much CPU and memory your app actually needs? Create a VPA in recommendation-only mode: it analyzes real behavior and provides recommendations without changing anything:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: myapp
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
updatePolicy:
updateMode: "Off"
# After a few minutes: check recommendations
kubectl describe vpa myapp -n my-namespace
For automatic adjustment, switch updateMode to InPlaceOrRecreate. Pods are resized in place where possible, and recreated where it is not.
Putting It All Together: A Production-Ready Platform
Concrete scenario: you’re running a SaaS application with an API, background worker, and web frontend.
What runs in your namespace:
- API Deployment with HPA (2–10 replicas depending on load)
- Worker Deployment for async jobs
- PostgreSQL via CloudNativePG for the primary database
- Dragonfly as session and cache layer
- S3 bucket for user uploads and media
- HTTPRoute with automatic TLS certificate
What you don’t manage:
- Cluster updates and node patches
- cert-manager, gateway controller, monitoring stack
- Backup infrastructure and storage provisioning
- Network policies and security patches
Everything is deployed via GitOps through ArgoCD. A git push rolls out changes automatically. A git revert undoes them.
Conclusion
A Kubernetes namespace on ITSH is more than a place to run containers. Managed databases, automatic HTTPS, persistent storage, and autoscaling make it a full production platform, without you having to manage cluster infrastructure.
The work stays where it belongs: on your application. The platform handles the rest.