Skip to main content
This guide provides step-by-step instructions for deploying Draftable API Self-Hosted v3 on AWS EKS with S3 storage.
AWS Only: Kubernetes deployment of Draftable API Self-Hosted v3 is currently supported on AWS EKS only. Azure AKS is not officially supported — while it is technically possible to use an NFS shared volume (e.g. Azure NetApp Files) with FILE_STORAGE_TYPE=local, Draftable does not test or optimise against NFS and cannot provide support for NFS-related issues. See FILE_STORAGE_TYPE for details.
S3 Storage Only: This guide covers S3-based deployments. EFS storage configurations are not covered in this documentation and are not recommended for Draftable deployments.

Prerequisites

Architecture

AWS Setup

EKS Cluster

S3 & IAM

Deploy Draftable

DNS & HTTPS

Troubleshooting


Architecture Overview

Component Architecture

Draftable API Self-Hosted consists of several microservices that work together:

Stateful vs Stateless Recommendation

Critical Data Loss Risk: The stateful components (PostgreSQL, Redis, RabbitMQ) included in this guide use Kubernetes PersistentVolumeClaims for storage. However, if pods are restarted, rescheduled, or the cluster is rebuilt, you may lose all your data including the database, cached sessions, and queued messages.For production deployments, you must use managed AWS services for stateful components.
Why managed services?
  • Kubernetes frequently tears down and recreates containers—excellent for stateless services but dangerous for stateful workloads
  • Pod restarts, node failures, or cluster updates can result in permanent data loss
  • Managed services provide automated backups, high availability, and data persistence guarantees
Redis persistence is especially critical. Redis stores the activated license and custom fonts. If Redis data is lost, all users will be locked out until the license is reactivated by re-running the web-init job. Always use persistent storage for Redis — or preferably, a managed Redis service with persistence enabled.

S3 Authentication Architecture

Critical: The Compare service does NOT support IRSA (IAM Roles for Service Accounts). It requires explicit AWS credentials.
The Compare service (.NET application) is missing the AWSSDK.SecurityToken assembly required for AssumeRoleWithWebIdentity. Attempting to use IRSA will result in:

Prerequisites

Required Information

Before starting, gather the following:
  • AWS Account ID
  • Desired AWS Region (e.g., ap-southeast-2)
  • Domain name for the application
  • Draftable product license key

Required Tools

Install and configure the following tools on your workstation: Verify installations:

Hardware Requirements

Your EKS cluster nodes should meet these minimum requirements:
  • Instance Type: t3.large or larger (2 vCPU, 8 GB RAM per node)
  • Node Count: Minimum 3 nodes for production
  • Storage: 50 GB per node

Estimated Deployment Time


AWS Account Setup

IAM Access Configuration

Recommendation: Instead of creating a dedicated IAM user for EKS access, grant access to your existing administrator role (e.g., SSO Administrator). This allows you to use your regular admin credentials with kubectl and view resources in the AWS Console.
After cluster creation, add your administrator role via EKS Access Entries:
  1. Go to AWS ConsoleEKS → Your Cluster → Access tab
  2. Click Create access entry
  3. Select your SSO Administrator role (or equivalent)
  4. Click Next
  5. Add access policy: AmazonEKSClusterAdminPolicy
  6. Click Create

Option B: Create Dedicated IAM User

If you prefer a dedicated user, it needs these permissions:
  • AdministratorAccess (recommended for initial setup)
Or these specific policies:
  • AmazonEKSClusterPolicy
  • AmazonEKSServicePolicy
  • AmazonEC2FullAccess
  • AmazonVPCFullAccess
  • AWSCloudFormationFullAccess
  • IAMFullAccess

Configure AWS CLI


Create EKS Cluster

Cluster Configuration

Download or create the cluster configuration file:

eksctl-cluster-config.yaml

EKS cluster configuration for Draftable
Key configuration points:
Kubernetes Version: Always use the latest stable version. Check available versions with:

Create the Cluster

This command takes 15–25 minutes to complete. Do not interrupt the process.

Configure kubectl

After cluster creation, configure kubectl to connect:
To view cluster resources in the AWS Console:
  1. Go to AWS ConsoleEKSdraftable-eksAccess tab
  2. Click Create access entry
  3. Select your SSO Administrator role
  4. Add policy: AmazonEKSClusterAdminPolicy
  5. Click Create

Verify Cluster

You should see 3 nodes in Ready state.

Create S3 Bucket

Create the Bucket

Configure CORS

CORS is required for the document viewer to load files from S3. Without CORS, comparisons will fail to render in the browser.
Download or create the CORS configuration:

s3-bucket-cors.json

S3 CORS configuration
Update the AllowedOrigins to match your domain:
Apply the CORS configuration:

Configure IAM for S3 Access

Draftable requires two types of AWS credentials:
  1. IRSA (IAM Role for Service Accounts) – Used by Web, Celery Worker, and Celery Beat
  2. IAM User with Access Keys – Required by the Compare service (does not support IRSA)

Get OIDC Provider ID

Create S3 Access Policy

Download or create the policy:

s3-access-policy.json

IAM policy for S3 access
Update the bucket name in the policy, then create it:

Create IAM Role for IRSA (Web/Celery)

Download or create the trust policy:

irsa-trust-policy.json

IRSA trust policy template
Update the following placeholders:
  • YOUR_ACCOUNT_ID – Your AWS account ID
  • YOUR_REGION – Your AWS region
  • YOUR_OIDC_ID – The OIDC ID from the previous step
Create the role:

Create IAM User for Compare Service

Required: The Compare service cannot use IRSA and must have explicit AWS credentials.
Save the AccessKeyId and SecretAccessKey from the output – you will need these when creating Kubernetes secrets.

Install AWS Load Balancer Controller

The AWS Load Balancer Controller is required to create Application Load Balancers (ALB) for the Ingress.

Create IAM Policy

Create Service Account

Install Controller via Helm

Verify the controller is running:

Deploy Draftable

Kubernetes Manifests

All Kubernetes manifests are available in our GitHub repository:

Kubernetes Manifests for EKS + S3

Complete set of deployment manifests
Templated Files: All YAML manifests are deliberately templated and incomplete. You must replace placeholder values (marked with YOUR_* or REPLACE_*) with your own configuration before applying them to your cluster.

Update Configuration Files

Before deploying, update these files with your values:

02-configmap.yaml

Critical S3 Configuration: The FILE_STORAGE_TYPE must be set to s3. Without this, the application uses local filesystem storage regardless of other S3 settings.

03-service-account.yaml

30-ingress.yaml

Create Secrets

1

Create namespace

2

Create application secrets

3

Create AWS credentials for Compare service

Deploy Infrastructure

1

Apply configuration

2

Deploy infrastructure services

3

Wait for infrastructure pods

Run Database Migrations

Deploy Application

Converter Health Probes: The 25-converter.yaml manifest must use TCP socket probes (not HTTP). The JODConverter API returns 404 on all health endpoints, and LibreOffice takes 60-90 seconds to start. Using HTTP probes will cause the pod to enter CrashLoopBackOff with hundreds of restarts. See Troubleshooting for details.
Converter startup time: The converter pod may take 90-120 seconds to become ready. During startup, you may see “Office process died with exit code 81” in the logs—this is normal while LibreOffice initializes.

Deploy Ingress


Configure DNS and HTTPS

Get ALB DNS Name

The ADDRESS column shows the ALB DNS name.

Create DNS Record

In your DNS provider, create a CNAME record:

Request ACM Certificate

If you haven’t already created an ACM certificate:
  1. Go to AWS Certificate Manager in the AWS Console
  2. Click Request a certificate
  3. Select Request a public certificate
  4. Enter your domain name
  5. Choose DNS validation
  6. Complete the DNS validation process
  7. Update 30-ingress.yaml with the certificate ARN

Verification

Check Pod Status

All pods should show Running status with 1/1 or 2/2 ready. Expected output:

Check S3 Connectivity

After creating a comparison, verify files are stored in S3:

Access the Application

Open https://your-domain.example.com in your browser. You should see the Draftable login page.

Troubleshooting

Pods Stuck in Pending

Cause: PersistentVolumeClaims not binding. Solution: Check StorageClass exists:
Ensure you’re using gp2 (default on EKS). If using gp3, you may need to create the StorageClass first.

CreateContainerConfigError

Cause: ConfigMap or Secret key not found. Solution: Verify all ConfigMap and Secret keys exist:

Compare Fails with AWSSDK.SecurityToken could not be found

Cause: Compare service is attempting to use IRSA (not supported). Solution:
  1. Ensure Compare uses serviceAccountName: default (not draftable-s3-sa)
  2. Verify aws-s3-credentials secret exists with AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY

Viewer Doesn’t Render Documents (CORS Error)

Cause: S3 bucket CORS not configured. Solution: Apply CORS configuration:

DisallowedHost Error in Web Logs

Cause: ALB health checks use the pod’s internal IP address, not the domain name. Solution: Ensure ALLOWED_HOSTS: "*" is set in the ConfigMap (already configured in the provided manifests).

REDIS_PORT Parse Error (invalid literal for int())

Cause: Kubernetes auto-creates REDIS_PORT=tcp://ip:port when a service is named redis. Solution: The Redis service is named redis-svc in our manifests to avoid this conflict. Ensure you’re using the provided manifests.

Converter in CrashLoopBackOff (400+ Restarts)

Cause: HTTP health probes fail because JODConverter returns 404 on all health endpoints, and LibreOffice takes 60-90 seconds to initialize. Symptoms:
  • Pod restarts continuously (often 400+ times)
  • Events show: Readiness probe failed: HTTP probe failed with statuscode: 404
  • Logs show: Office process died with exit code 81; restarting it
Solution: The converter must use TCP socket probes, not HTTP probes. Ensure your 25-converter.yaml uses:
Do NOT use httpGet probes for the converter. The JODConverter REST API returns 404 on /, /health, and /actuator/health endpoints, causing probe failures and endless restart loops.
Why TCP probes work: TCP probes simply verify port 8080 is listening, which it is once Tomcat starts—even while LibreOffice is still initializing. Note: During normal startup, you will see “Office process died with exit code 81” messages in the logs. This is expected behavior while LibreOffice initializes.

Useful Debugging Commands


Production Recommendations

Using Managed AWS Services

For production deployments, replace in-cluster stateful services with managed AWS services:

Amazon RDS for PostgreSQL

  1. Create RDS PostgreSQL instance (Engine: PostgreSQL 16, Multi-AZ enabled)
  2. Update ConfigMap:
  3. Skip applying 10-postgresql.yaml

Amazon ElastiCache for Redis

  1. Create ElastiCache Redis cluster (Engine: Redis 7.x)
  2. Update ConfigMap:
  3. Skip applying 12-redis.yaml

Amazon MQ for RabbitMQ

Amazon MQ must use the RabbitMQ engine. Amazon MQ for ActiveMQ is not compatible with Draftable (it uses AMQP 1.0; Draftable requires AMQP 0-9-1), and AWS SQS is not supported. See supported message brokers. If your RabbitMQ or Redis uses a private or internal CA, see Using a private or internal CA.
  1. Create Amazon MQ RabbitMQ broker
  2. Update ConfigMap:
  3. Skip applying 11-rabbitmq.yaml

Configuration Reference

S3 Environment Variables

Application Environment Variables

For a complete list of environment variables, see the Docker Compose Guide.

Support

If you encounter issues during deployment, please contact us at support@draftable.com with:
  • Pod logs (kubectl logs -n draftable deployment/<pod-name>)
  • Pod events (kubectl describe pod -n draftable -l app=<app-name>)
  • ConfigMap values (sanitized)
  • Error messages from the browser console