Why we do not recommend this platform
Two limitations sit behind that warning. Neither is a bug, and neither can be worked around with configuration.Draftable has no Azure Blob Storage backend
API Self-Hosted supports exactly two file storage backends: local filesystem and Amazon S3. Azure Blob Storage is not supported, and there is no way to point Draftable’s S3 client at a non-AWS, S3-compatible endpoint. The practical consequence is that on AKS you must useFILE_STORAGE_TYPE=local backed by a shared
network file share, because the web, Celery worker, Celery beat and compare services all read and
write the same files at the same time, from potentially different nodes.
Object storage is what this product is designed around on Kubernetes. A network file share works,
but it is emulating a local disk over a network, and that carries a measurable cost described in
Storage performance.
Azure has no managed RabbitMQ
Draftable requires AMQP 0-9-1. Azure Service Bus implements AMQP 1.0 and is therefore not compatible, and Azure offers no managed RabbitMQ service of any kind. On AWS you can use Amazon MQ for RabbitMQ and keep your message broker outside the cluster. On Azure you must run RabbitMQ yourself, either inside the cluster or on a dedicated virtual machine. That is a genuine reduction in resilience compared with our AWS reference architecture, and it means you own the broker’s availability and backups.PostgreSQL and Redis have good managed equivalents on Azure (Azure Database for PostgreSQL Flexible
Server and Azure Cache for Redis) and you should use them. RabbitMQ is the exception.
Choosing a different platform
In rough order of how well we support them:Docker Compose on a Linux VM
Best supported option on Azure. Run an Azure Linux virtual machine with Docker Compose. Local
storage is genuinely local, so none of the file share overhead applies.
AWS EKS with S3
The fully supported Kubernetes option. If your organisation can use AWS, this is the
architecture the product is designed for, and the only Kubernetes storage configuration we support.
Generic Kubernetes
Platform-agnostic manifests and configuration reference. This article builds on it — start
there for the base manifests.
Azure AKS
Not recommended. This article. Functional and verified, but on unsupported storage. Choose it
only if the three options above are genuinely unavailable to you.
What we tested, and what we did not
We would rather you knew the limits of our testing than assumed more than we can support. Verified working:- AKS 1.35, two
Standard_D2s_v5nodes,australiaeast - Azure Files Premium with the NFS v4.1 protocol as the shared
ReadWriteManyvolume - API Self-Hosted v3 using the
latestimage tags as of 3 August 2026 (apish-web3.0.8 was current at that date), with in-cluster PostgreSQL 16, RabbitMQ 3.13.3 and Redis 8 - Comparisons of Word and PDF documents completing end to end via the API
- The comparison results viewer rendering correctly in a browser
- Public HTTPS via ingress-nginx and a Let’s Encrypt certificate
- Sustained load over hours or days. Our testing covered a single session.
- Azure NetApp Files, which may perform better than Azure Files and which some customers prefer for exactly this kind of workload.
- Azure Files Premium with SMB for full comparison workloads. We measured its raw file operations, which were worse than NFS, and did not proceed further.
- Multi-region, private-endpoint-only, or restricted-egress network topologies.
- Horizontal scaling beyond a single replica of each service.
Storage performance
This is the part we most want you to understand before committing. We measured raw file operations on a live AKS cluster, comparing the node’s local disk against both Azure Files Premium protocols.
Bulk throughput is not the problem. Sequential write bandwidth on Azure Files Premium measured
around 226 MiB/s, comfortably faster than the node’s own OS disk. If you benchmark this deployment
with large files, you will conclude that storage is fast, and you will be measuring the wrong thing.
Per-file overhead is the problem. Creating a single file costs roughly 10 ms on NFS and 17 ms
on SMB, against microseconds on a local disk. Draftable’s workload is full of small file
operations: a
.docx is a ZIP archive of many small parts, and comparing two documents means
unpacking all of those parts, writing page renderings, scroll maps and change data, then cleaning up.
What that means in practice
The cost scales with the number of files a comparison touches, not with document size. That is why the deployment is viable at all, rather than a reason to choose it:
Both part counts are measured, not estimated. The 5.8 second figure held to within 0.2 seconds across
every run of our final clean test pass.
Comparison times above were measured on a deliberately small two-node test cluster (4 vCPU total)
and are not a performance prediction for your environment. They are included only to show that
ordinary documents remain workable. Your times will differ.
- Ordinary business documents — tens of internal parts — remain workable. Every comparison still pays overhead that a supported deployment would not, but it is unlikely to be the thing you notice.
- Documents with very large numbers of embedded objects — hundreds or thousands of images or attachments — will feel it, and the cost grows linearly with part count.
- High comparison concurrency multiplies file operations against a single shared file share, which becomes the bottleneck before CPU does.
Always choose NFS, never SMB
SMB is the default protocol for Azure Files. Do not use it. Our measurements show SMB is around 1.6 times slower than NFS at creating files and roughly twice as slow at deleting them, and Draftable is a Linux application that expects POSIX filesystem behaviour, which NFS provides more faithfully. NFS requires the Premium tier. Standard Azure Files does not offer NFS.Prerequisites
Start from the generic Kubernetes Deployment Guide for base manifests, environment variables and the overall component architecture. This article covers only what differs on Azure. Tools: Azure CLI,kubectl, and Helm.
Azure prerequisites:
- A subscription with quota for at least 8 vCPU in your chosen region for a production-shaped cluster. Check both your quota and your subscription’s permitted VM SKU list, which are separate restrictions — a size can be blocked even when quota is available.
- Permission to create role assignments, not just resources.
Contributoralone is insufficient; you needOwner, orContributorplusUser Access Administrator. - A Draftable product key, and outbound HTTPS access to
draftable.comfor licence activation.
Azure-specific configuration
Six things differ from a generic Kubernetes deployment. The first will stop your deployment working entirely and is very hard to diagnose, so start there.1. Load balancer health probes
Theingress-nginx Helm chart sets appProtocol: http on the controller’s Service ports. The Azure
cloud provider reads that and creates an HTTP health probe against / rather than a TCP probe.
Other ingress controllers may behave the same way, so check yours if you are not using
ingress-nginx.
Your ingress controller answers / with a 404 when no ingress rule matches the probe’s request,
because the probe sends no Host header. Azure load balancer health probes treat only HTTP 200
as healthy. Every node is therefore marked unhealthy and the load balancer silently discards all
inbound traffic.
The symptom is a connection timeout from the internet while the cluster is completely healthy
internally. If you are using Let’s Encrypt, certificate issuance fails with
Timeout during connect (likely firewall problem), which naturally sends you to inspect network
security groups. The network security group is not the problem.
Fix it with an annotation on the ingress controller Service:
/healthz is served on the controller’s data ports and returns 200.
To confirm what your probe is actually configured to check:
2. The shared NFS StorageClass
nconnect=4 opens multiple TCP connections to the share and is the single most useful tuning option
for this workload. actimeo=30 caches attribute lookups and reduces round trips considerably; if you
see unexpected file consistency behaviour between services, reduce it.
The minimum provisioned size for an Azure Files Premium share is 100 GiB, which is a billing
floor rather than a capacity recommendation.
/srv/draftable in the web, Celery worker, Celery beat and compare pods, and set:
3. Create the storage subdirectories before the compare service starts
The compare service watches/srv/draftable/config for licence changes and fails to start if that
directory does not exist. A freshly provisioned file share is empty, and nothing creates it for you.
On Docker Compose the directory is present inside the container image, so this problem is unique to a
new network share.
Add an init container to the compare deployment:
4. Compare service memory
When the compare service is terminated, every comparison it was processing is lost. Callers see either a comparison that never completes or an HTTP 500 on their next status request. This looks exactly like a storage or network fault, so check for it first:5. Database connection limits
Draftable holds persistent database connections by default:DB_CONN_MAX_AGE defaults to -1,
which means connections are kept open indefinitely, and each worker process maintains its own.
Under sustained API load, connection count grows and a database configured with too few connections
will start refusing them. The API returns HTTP 500 and the logs show:
DB_CONN_MAX_AGE to a bounded value such as 300 to recycle
connections instead of holding them open indefinitely.
To check current usage:
6. Converter health probes
This applies to all Kubernetes platforms, but it is worth repeating because it produces a dramatic failure. The document converter must use TCP probes, not HTTP. Its API returns 404 on every health endpoint, and LibreOffice takes 60 to 90 seconds to initialise, so HTTP probes fail permanently and the pod restarts hundreds of times.Office process died with exit code 81 in the converter log during startup is expected and not an
error.
DNS and TLS
For a publicly reachable hostname without configuring your own DNS, Azure can assign a label to the load balancer’s public IP address, giving you<label>.<region>.cloudapp.azure.com:
APP_BASE_URL and CSRF_TRUSTED_ORIGINS to the final HTTPS URL, and keep ALLOWED_HOSTS broad
enough to accept the load balancer’s IP-based health checks.
Verification
Confirm the licence actually activated. The initialisation job completes successfully even when activation fails, so a completed job is not proof:/srv/draftable/data on the shared volume.
Draftable stores the activated licence in Redis. A copy is written to
/srv/draftable/config/license.json only when that directory already exists, so on a newly
provisioned file share you should not expect to find it — do not treat its absence as a failed
activation.Redis persistence is therefore mandatory, not optional. If Redis loses its data, users are locked
out until the initialisation job is re-run to reactivate the licence.Troubleshooting
The site times out from the internet but the cluster looks healthy
The site times out from the internet but the cluster looks healthy
Almost always the load balancer health probe issue described in
Load balancer health probes. Confirm by testing the ingress
controller from inside the cluster — if that works, the fault is between the internet and the load
balancer, not in your cluster.
Comparisons fail intermittently under load
Comparisons fail intermittently under load
Check these in order before suspecting storage:
- Compare service terminated for memory — see Compare service memory.
- Database connections exhausted — see Database connection limits.
- Only then consider file share throughput.
Comparisons are slower than expected
Comparisons are slower than expected
Confirm your share uses NFS, not SMB, and that it is on the Premium tier. Check
nconnect=4
is present in your mount options. If your documents contain very large numbers of embedded objects,
the per-file overhead described in Storage performance may be inherent to
this platform rather than a misconfiguration.The compare service will not start
The compare service will not start
Check that
/srv/draftable/config exists on the shared volume. See
Create the storage subdirectories.Cluster creation fails on VM size
Cluster creation fails on VM size
Your subscription’s permitted SKU list is separate from your quota, and a size can be rejected even
when quota shows capacity. The error message lists every permitted size for your subscription and
region.

