Azure Installation (Helm)

This guide walks through deploying JuliaHub as a self-managed installation on Microsoft Azure using Helm. By the end, you will have a running JuliaHub platform on an AKS cluster with Azure Files storage and an external PostgreSQL database.

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

Obtain a TLS certificate and private key in PEM format. The 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. You can provide the certificate in 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
Azure CLI (az)2.50Azure resource management
Terraform1.5Infrastructure provisioning (Option A only)
Helm3.0Kubernetes package manager
kubectl1.28Kubernetes CLI

Log in to Azure:

az login
az account set --subscription <your-subscription-id>

Infrastructure Setup

Choose one of the following paths depending on whether you are starting from scratch or have an existing AKS cluster.

Option A: Using JuliaHub Terraform Modules

JuliaHub provides public Terraform modules that provision all required Azure infrastructure: a VNet with private subnets, an AKS cluster, PostgreSQL Flexible Server, and Azure Files Premium storage.

1. Clone the modules:

git clone https://github.com/JuliaComputing/platform-public-terraform-modules.git
cd platform-public-terraform-modules/azure

2. Create your configuration:

cp terraform.tfvars.example terraform.tfvars

Edit terraform.tfvars to set your values. At minimum, update:

azure_subscription_id = "<your-subscription-id>"
resource_group_name   = "juliahub-rg"
location              = "eastus"       # your preferred region

# Storage account names must be globally unique, 3-24 lowercase alphanumeric
files_storage_account_name = "<globally-unique-name>"
blob_storage_account_name  = "<globally-unique-name>"

See the repository README for the full set of configurable variables.

3. Apply the infrastructure:

terraform init
terraform apply

This takes approximately 15–20 minutes. Once complete, Terraform outputs the connection details needed for later steps.

4. Connect to the AKS cluster:

az aks get-credentials \
  --resource-group "$(terraform output -raw resource_group_name)" \
  --name "$(terraform output -raw aks_cluster_name)"

5. Create the Azure Files StorageClass and Secret:

The Helm chart requires a StorageClass and a Kubernetes Secret for Azure Files access:

terraform output -raw storage_files_kubernetes_storage_class_yaml | kubectl apply -f -

kubectl create secret generic azure-files-secret \
  --from-literal=azurestorageaccountname="$(terraform output -raw storage_files_account_name)" \
  --from-literal=azurestorageaccountkey="$(terraform output -raw storage_files_primary_access_key)"

Continue to Install JuliaHub.

Option B: Existing AKS Cluster

If you already have an AKS cluster, ensure the following resources are available and accessible from the cluster's VNet:

ResourceRequirements
AKS clusterKubernetes 1.28+, Azure CSI drivers enabled for Disk and Files
PostgreSQLAzure Database for PostgreSQL Flexible Server (or compatible), accessible from the AKS VNet
Azure Files PremiumStorage account with NFS-enabled file shares: one for platform configuration, one for compute userdata

1. Create the StorageClass:

Create a StorageClass for Azure Files Premium NFS. Adjust the skuName and resourceGroup to match your environment:

# azure-files-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: azurefile-csi-premium-jh
provisioner: file.csi.azure.com
parameters:
  skuName: Premium_LRS
  protocol: nfs
  resourceGroup: <your-resource-group>
  storageAccount: <your-storage-account>
reclaimPolicy: Retain
volumeBindingMode: Immediate
mountOptions:
  - nconnect=4
  - noresvport
  - actimeo=1
kubectl apply -f azure-files-storageclass.yaml

2. Create the Azure Files Secret:

kubectl create secret generic azure-files-secret \
  --from-literal=azurestorageaccountname="<your-storage-account>" \
  --from-literal=azurestorageaccountkey="<your-storage-account-key>"

Install JuliaHub

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 Azure. If you used the Terraform modules (Option A), you can retrieve most values from Terraform outputs.

hostname: '<your-hostname>'

# Option 1: Inline TLS certificate
tlsFullchainPem: |
  <contents of fullchain.pem, indented 2 spaces>
tlsPrivkeyPem: |
  <contents of privkey.pem, indented 2 spaces>

# Option 2: Use an existing Kubernetes TLS Secret (instead of the above)
# certsSecretName: "<tls-secret-name>"

configDirectory:
  type: "azure-files"
  azureFiles:
    volumeName: "juliahub-config-pv"
    storageClassName: "azurefile-csi-premium-jh"
    shareName: "<config-file-share-name>"
    server: "<storage-account>.privatelink.file.core.windows.net"
    resourceGroup: "<resource-group>"
    storageAccount: "<storage-account>"

postgres:
  type: external
  external:
    username: "<postgres-username>"
    password: "<postgres-password>"
    database: "<postgres-database>"
    host: "<postgres-server-fqdn>"

compute:
  userdataDirectory:
    type: "azure-files"
    azureFiles:
      volumeName: "juliahub-userdata-pv"
      storageClassName: "azurefile-csi-premium-jh"
      shareName: "<userdata-file-share-name>"
      server: "<storage-account>.privatelink.file.core.windows.net"
      resourceGroup: "<resource-group>"
      storageAccount: "<storage-account>"
Using Terraform outputs

If you used Option A, you can populate the Azure-specific values from Terraform:

terraform output -raw storage_files_account_name  # storageAccount
terraform output -raw storage_files_server         # server
terraform output -raw resource_group_name          # resourceGroup
terraform output -raw postgresql_server_fqdn       # postgres host
terraform output -raw postgresql_administrator_login    # postgres username
terraform output -raw postgresql_administrator_password # postgres password
terraform output -raw postgresql_database_name     # postgres database

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 as container images are pulled and services start up.

DNS Configuration

Once the installation is complete, get the external IP of the load balancer:

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

Create the following DNS A records pointing to this IP:

RecordTypeValue
<hostname>A<load-balancer-ip>
*.apps.<hostname>A<load-balancer-ip>
docs.<hostname>A<load-balancer-ip>
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 authentication providers (OIDC, SAML, LDAP) in the JuliaHub admin settings
  • Set up package synchronization to pull packages from the General registry
  • Review the full Helm values reference for additional configuration options