AWS Installation (Helm)

This guide walks through deploying JuliaHub as a self-managed installation on Amazon Web Services using Helm. By the end, you will have a running JuliaHub platform on an Amazon Elastic Kubernetes Service (EKS) cluster with Amazon EFS storage and an external PostgreSQL database.

Terraform modules are not yet available for AWS

JuliaHub does not yet publish Terraform modules for AWS. This guide therefore describes the AWS resources you need to provision (with any tool you prefer — the AWS console, eksctl, the AWS CLI, your own Terraform, or CloudFormation) and how to wire each one into the Helm chart values. Public AWS Terraform modules are planned for a future release; until then, treat the resource list below as the contract the Helm chart expects.

Prerequisites

Before starting, ensure you have the following:

Hostname

Decide on a hostname for your JuliaHub installation (e.g. juliahub.example.com). You will need to create DNS records after the installation is complete, so the hostname does not need to resolve yet, but it must be decided now as it is baked into the TLS certificate and Helm values.

TLS Certificate

The TLS certificate must cover the following domains:

  • <hostname> — the main platform URL
  • *.apps.<hostname> — JuliaHub-managed application routing
  • docs.<hostname> — generated Julia package documentation

A wildcard certificate for *.<hostname> plus the bare <hostname> is the simplest option.

Recommended: offload TLS to an ALB with an ACM certificate. On AWS the common approach is to terminate TLS at an Application Load Balancer using a certificate from AWS Certificate Manager (ACM). You request (or import) the certificate in ACM, reference its ARN on the ALB Ingress, and set offloadTLS: true so the chart does not manage certificates itself. This keeps certificate issuance and renewal in ACM rather than in Helm values. This is the default path in this guide — see Exposing the platform.

Alternative: supply the certificate to the chart. If you are not using an ALB (for example, you expose the platform through a LoadBalancer Service and terminate TLS in the cluster), obtain the certificate and private key in PEM format and provide them in one of two ways:

  • Inline in Helm values: Save the full certificate chain as fullchain.pem and the private key as privkey.pem, then set tlsFullchainPem and tlsPrivkeyPem in your values file.
  • As a Kubernetes TLS Secret: Create a kubernetes.io/tls Secret in the target namespace and set certsSecretName in your values file. When using this method, tlsFullchainPem and tlsPrivkeyPem do not need to be set.

Replicated License

JuliaHub self-managed installations are distributed through Replicated. Contact your JuliaHub sales representative to obtain:

  • A license ID (used as the Helm registry password)
  • A license email (used as the Helm registry username)

Initial User Credentials

JuliaHub will provide initial admin credentials for your installation. These can be overridden by setting settings.initialUsers in your Helm values with a bcrypt hashed password.

CLI Tools

Install the following on your workstation:

ToolMinimum VersionPurpose
AWS CLI (aws)2.13AWS resource management
eksctl0.170EKS cluster provisioning (optional, convenience)
Helm3.0Kubernetes package manager
kubectl1.28Kubernetes CLI

You will also need the AWS Load Balancer Controller installed in the cluster for the recommended ALB exposure path (see Exposing the platform).

Log in to AWS:

aws configure          # or export AWS_PROFILE / use SSO
aws sts get-caller-identity   # confirm the right account and region

AWS Resources to Provision

Because Terraform modules are not yet published, you must create the following AWS resources yourself before installing the chart. The rightmost column shows which Helm value each resource maps to; the mappings are collected into a single values file in Create Your Values File.

VPC and subnet planning

Before provisioning, review AWS's EKS VPC and subnet requirements and the VPC and subnet considerations in the EKS Best Practices Guide. In particular, plan for enough IP address space for pods and jobs, and — if you use the recommended ALB path — tag the load balancer subnets (kubernetes.io/role/elb on public subnets or kubernetes.io/role/internal-elb on private subnets) so the AWS Load Balancer Controller can discover them.

ResourceWhy it is neededMaps to Helm value
VPC + subnetsNetwork for the EKS cluster and its load balancer. Use private subnets for nodes and public (or internal) subnets for the load balancer, across at least two Availability Zones. See EKS VPC and subnet requirements.(infrastructure only — not a chart value)
EKS clusterRuns the JuliaHub platform. Kubernetes 1.28+.Target cluster for helm install
Managed node group(s)Baseline worker nodes for the platform's own pods.(infrastructure only)
Node autoscaler(compute only)Brings worker nodes up and down on demand for jobs. Karpenter is recommended (JuliaHub uses it); Cluster Autoscaler is a lighter alternative. See Node Provisioning for Jobs.(cluster add-on)
EFS filesystem + access point(s)ReadWriteMany storage for the platform configuration directory (and, if compute is enabled, job userdata).configDirectory.efs.*, compute.userdataDirectory.efs.*
AWS EFS CSI driverKubernetes driver (efs.csi.aws.com) that mounts the EFS filesystem. The chart references this driver but does not install it.(cluster add-on)
PostgreSQL databasePrimary relational store. Amazon RDS for PostgreSQL (or Aurora PostgreSQL), reachable from the cluster's VPC.postgres.external.*
S3 bucket(compute only)Object storage for dataset and job results. Only needed when compute is enabled.compute.storage.aws.bucketName
IAM role for compute storage(compute only)Grants JuliaHub jobs access to the S3 bucket.compute.storage.aws.storageRoleArn
IAM role for the cloud host(compute only)Role JuliaHub assumes to launch compute workloads.compute.cloudhost.aws.roleArn
ACM certificateTLS certificate for the platform hostname(s), managed by AWS Certificate Manager and attached to the ALB. This is the recommended way to handle TLS on AWS.offloadTLS: true + ALB Ingress annotation
AWS Load Balancer ControllerProvisions the ALB from the Ingress resource and applies the ACM certificate and alb.ingress.kubernetes.io/* annotations. Required for the recommended ALB path.(cluster add-on)
Application Load BalancerExposes the platform to users and terminates TLS with the ACM certificate. Created automatically by the AWS Load Balancer Controller from the Ingress.websrvr.ingress.*
Route 53 (or your DNS provider)Resolves the platform hostname(s) to the load balancer. Created after install.See DNS Configuration

EFS Access Points

The chart mounts EFS through the efs.csi.aws.com CSI driver and creates the PersistentVolume itself from the values you supply — you do not create a Kubernetes StorageClass for EFS. You do, however, need to provision the EFS filesystem and (recommended) an EFS access point yourself:

  • The config directory requires one EFS filesystem. An access point is optional; when set, the PV volume handle becomes <filesystemId>::<accessPointId>.
  • The compute userdata directory (only when compute is enabled) requires an access point. The mount roots at the access point's directory, and JuliaHub builds per-job volumes from the filesystem ID and access point ID.

Ensure the EFS filesystem has mount targets in the subnets your node group runs in, and that its security group allows NFS (TCP 2049) from the node security group.

Install JuliaHub

Connect to the EKS Cluster

aws eks update-kubeconfig --name <cluster-name> --region <region>
kubectl get nodes   # confirm connectivity

Install the AWS EFS CSI Driver

The chart mounts EFS via efs.csi.aws.com but does not install the driver. Install it as an EKS add-on (or via its Helm chart) if it is not already present:

aws eks create-addon \
  --cluster-name <cluster-name> \
  --addon-name aws-efs-csi-driver \
  --region <region>

See the AWS EFS CSI driver documentation for IAM setup (an IRSA role for the driver's service account) and prerequisites.

Log in to the Replicated Registry

helm registry login registry.replicated.com \
  --username <license-email> \
  --password <license-id>

View the Full Values Reference

To see all configurable Helm values and their defaults:

helm show values oci://registry.replicated.com/juliahub/production/juliahub-platform

Create Your Values File

Create a myvalues.yaml with the minimal configuration for AWS. Fill in the identifiers of the resources you provisioned above.

hostname: '<your-hostname>'

# Recommended: terminate TLS at the ALB with an ACM certificate.
# The chart does not manage certificates; the ALB Ingress below references the
# ACM cert by ARN. See "Exposing the Platform" for the full websrvr.ingress block.
offloadTLS: true

# Alternative (only if you are NOT offloading TLS to an ALB): supply the
# certificate to the chart. Do not set these when offloadTLS is true.
# tlsFullchainPem: |
#   <contents of fullchain.pem, indented 2 spaces>
# tlsPrivkeyPem: |
#   <contents of privkey.pem, indented 2 spaces>
# certsSecretName: "<tls-secret-name>"   # or reference an existing TLS Secret

# Platform configuration directory on EFS.
# The chart creates the PV/PVC; you supply the EFS filesystem (and, optionally,
# an access point). Requires the efs.csi.aws.com driver to be installed.
configDirectory:
  type: "efs"
  efs:
    filesystemId: "<fs-xxxxxxxxxxxxxxxxx>"
    accessPointId: "<fsap-xxxxxxxxxxxxxxxxx>"   # optional for the config dir
    useIAM: false                               # set true to mount with IAM auth

# External PostgreSQL (e.g. Amazon RDS for PostgreSQL).
postgres:
  type: external
  external:
    username: "<postgres-username>"
    password: "<postgres-password>"
    database: "<postgres-database>"
    host: "<rds-endpoint>"
    port: 5432
    requiresSSL: true
PostgreSQL over SSL

postgres.external.requiresSSL defaults to true. Amazon RDS presents a TLS certificate by default, so leave this enabled unless you have specifically disabled SSL on the database.

Compute Configuration

Whether compute (batch jobs and interactive applications) is enabled is determined by your Replicated license, not by a value you set — if your license does not include compute, these settings have no effect. If you are not sure whether your installation is entitled to compute, or you would like to add it, contact JuliaHub support to confirm your entitlement.

If compute is enabled, provide the following. It requires an EFS access point for job userdata, an S3 bucket, and the IAM roles listed in AWS Resources to Provision:

compute:
  # Per-job persistent storage on EFS. accessPointId is REQUIRED here.
  userdataDirectory:
    type: "efs"
    efs:
      filesystemId: "<fs-xxxxxxxxxxxxxxxxx>"
      accessPointId: "<fsap-xxxxxxxxxxxxxxxxx>"

  # S3 bucket for dataset and job results.
  storage:
    aws:
      bucketName: "<your-results-bucket>"
      storageRoleArn: "arn:aws:iam::<account-id>:role/<storage-role>"
      storagePrefix: "datasets"

  # Role JuliaHub assumes to launch compute workloads.
  cloudhost:
    aws:
      region: "<region>"
      roleArn: "arn:aws:iam::<account-id>:role/<cloudhost-role>"

# IAM Roles for Service Accounts (IRSA): let the platform assume AWS identity.
# The annotation is applied to the platform ServiceAccount and to the juliarun
# job ServiceAccount, so it grants the permissions needed to reach S3 and to
# assume the storage/cloudhost roles above.
serviceAccount:
  annotations:
    eks.amazonaws.com/role-arn: "arn:aws:iam::<account-id>:role/<juliahub-platform-role>"
Job images are pulled through Replicated

You do not need to create or host a container registry (such as Amazon ECR) for the base job images. Like the platform images, they are served through the Replicated registry, and the pull credentials are injected from your Replicated license automatically — so no registry values or image pull secrets need to be set for compute.

IRSA is how the platform reaches AWS

The role ARNs under compute.storage.aws and compute.cloudhost.aws are passed to the JuliaHub runtime, but the platform assumes AWS identity through the ServiceAccount annotation above. Configure an IRSA (or EKS Pod Identity) role whose trust policy allows the platform ServiceAccount to assume it, and which is permitted to assume the storage and cloud-host roles.

Get the IAM policy JSON from JuliaHub support

The exact permissions these roles require are install-specific (they depend on your bucket name, prefixes, account ID, and cluster OIDC provider) and they change from one JuliaHub release to the next, so this guide does not publish policy documents to copy. Contact JuliaHub support for the IAM policy JSON matching your JuliaHub version. The categories below are illustrative only — use them to plan, not as an authoritative policy:

  • Storage role (compute.storage.aws.storageRoleArn): scoped s3:* object and list access to the results bucket and its dataset/results prefixes.
  • Cloud-host role (compute.cloudhost.aws.roleArn): permissions to launch and manage compute (EC2, SSM, CloudWatch Logs, iam:PassRole) and to read job secrets.
  • Platform role (the IRSA/Pod Identity role above): sts:AssumeRole on the storage and cloud-host roles, EFS mount/access-point access, ECR read, and CloudWatch Logs.

Node Provisioning for Jobs

Compute jobs and interactive applications run as pods that are scheduled onto worker nodes on demand — a single job can request a large CPU or GPU instance that only needs to exist while the job runs. Your EKS cluster therefore needs a node autoscaler to bring nodes up and down as jobs are submitted, or jobs will stay Pending with nothing to run them.

  • Karpenter (recommended). JuliaHub runs Karpenter in its own managed clusters, so it is the best-tested option. Karpenter provisions right-sized nodes per pending pod and consolidates them when idle.
  • Cluster Autoscaler is a lighter-weight alternative that scales pre-defined managed node groups. It works, but JuliaHub does not run it itself, so Karpenter is preferred for the elastic, heterogeneous instance sizes that jobs request.

Jobs schedule onto whatever capacity the autoscaler provides, so no special node labels or taints are required — the autoscaler just needs to be able to launch nodes (including larger CPU and GPU instance types) for pending job pods.

Pinning platform pods to specific nodes

If you want to keep the platform's own pods on particular nodes (for example, a dedicated system node group separate from job capacity), the chart exposes a top-level nodeSelector and tolerations that apply to all pods, and each service accepts its own nodeSelector/tolerations that override the global ones. Set these in your values file to match the labels and taints on your chosen nodes.

Node pool definitions are environment-specific

JuliaHub's own Karpenter NodePool and EC2NodeClass manifests are internal to its managed infrastructure and are tuned per cluster (instance families, AMIs, capacity type, subnet/security-group discovery tags). They are not shipped as a copy-and-apply template. Work with JuliaHub support to size the CPU and GPU pools for your expected workloads.

Exposing the Platform

Option A — ALB Ingress with ACM (recommended). Front the platform with an Application Load Balancer provisioned by the AWS Load Balancer Controller, and let the ALB terminate TLS using an ACM certificate. Set offloadTLS: true (already set in the base values file above), disable the built-in LoadBalancer Service, and enable the Ingress with your ACM certificate ARN:

websrvr:
  external:
    enabled: false
  ingress:
    enabled: true
    className: "alb"
    annotations:
      alb.ingress.kubernetes.io/scheme: "internet-facing"
      alb.ingress.kubernetes.io/target-type: "ip"
      alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:<region>:<account-id>:certificate/<id>"
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
Prerequisites for the ALB path

The AWS Load Balancer Controller must be installed in the cluster (see Prerequisites), and the ACM certificate must be issued for <hostname> (and *.apps.<hostname> / docs.<hostname>, or a wildcard) in the same region as the ALB.

Option B — LoadBalancer Service. If you are not using an ALB, the chart can create a LoadBalancer Service named websrvr-external, which AWS fulfils with a load balancer. With this option TLS is terminated in the cluster, so supply the certificate to the chart (tlsFullchainPem/tlsPrivkeyPem or certsSecretName) and leave offloadTLS: false. To control the AWS load balancer (for example, to request a Network Load Balancer or an internal scheme), add annotations under websrvr.annotations — these are applied to the websrvr-external Service:

websrvr:
  external:
    enabled: true
    serviceType: "LoadBalancer"
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "external"
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "instance"
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"

Run the Installation

helm install juliahub-platform \
  oci://registry.replicated.com/juliahub/production/juliahub-platform \
  --namespace juliahub \
  --create-namespace \
  --timeout 30m \
  --wait \
  --wait-for-jobs \
  --values myvalues.yaml
Note

The initial installation may take up to 30 minutes. This includes pulling container images, starting up services, and the initial package registry sync.

DNS Configuration

Once the installation is complete, get the DNS name of the load balancer. For the recommended ALB Ingress path:

kubectl get ingress -n juliahub \
  -o jsonpath='{.items[0].status.loadBalancer.ingress[0].hostname}'

If you used the LoadBalancer Service instead, read it from the Service:

kubectl get svc -n juliahub websrvr-external \
  -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'

AWS load balancers are addressed by DNS name rather than a static IP, so create the following as CNAME records (or Route 53 alias records pointing at the load balancer):

RecordTypeValue
<hostname>CNAME / alias<load-balancer-dns-name>
*.apps.<hostname>CNAME / alias<load-balancer-dns-name>
docs.<hostname>CNAME / alias<load-balancer-dns-name>
Route 53 alias records

In Route 53, prefer alias records targeting the load balancer over CNAMEs — aliases work at the zone apex and incur no extra lookup. A wildcard alias covers *.apps.<hostname>.

Warning

DNS propagation may take time depending on your DNS provider. JuliaHub will not be accessible until these records resolve.

Verify the Installation

1. Check that all pods are running:

kubectl get pods -n juliahub

All pods should be in Running or Completed status.

2. Access JuliaHub:

Open https://<hostname> in your browser. You should see the JuliaHub login page.

Upgrading

To upgrade to a newer version of JuliaHub:

helm upgrade juliahub-platform \
  oci://registry.replicated.com/juliahub/production/juliahub-platform \
  --namespace juliahub \
  --timeout 30m \
  --wait \
  --wait-for-jobs \
  --values myvalues.yaml \
  --version <new-version>

Next Steps

  • Configure single sign-on (SSO) for your identity provider (OIDC, SAML, LDAP). The mechanism for configuring SSO on self-managed installations is still being finalized, so contact JuliaHub support to set it up for your installation.
  • Review the full Helm values reference for additional configuration options