Developer Tools Azure

Athens Go Module Proxy on Ubuntu 24.04 on Azure User Guide

| Product: Athens Go Module Proxy 0.18.1 on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of Athens Go Module Proxy on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Athens is an open source, enterprise ready implementation of the Go module proxy (the GOPROXY protocol built into the Go toolchain). Point your developers and CI pipelines at an Athens server and every go build, go test and go mod download fetches modules through it. Athens downloads each module version once, caches it on disk, and serves every later request from that cache: faster builds, far less duplicated download traffic, and reproducible builds that keep working even when an upstream module is deleted, retagged, or its VCS host is unreachable.

The image installs Athens 0.18.1 from the official upstream release (a single self contained Go binary) together with the official Go 1.26.5 toolchain, because Athens shells out to the go command to fetch modules on a cache miss. Athens has no web interface: it speaks the Go module download HTTP protocol and is driven by the go command. In this image the Athens server is bound to the loopback interface only (127.0.0.1:3000) and nginx is the sole network facing surface. nginx terminates TLS on port 443 and fronts the entire proxy with HTTP basic authentication, so the proxy is never an open module relay.

Secure by default, no shared credentials. Athens ships with no authentication of its own, which means an unprotected Athens is an open proxy. This image never exposes it that way. Nothing secret is baked into the image. On the first boot of every instance, Athens generates a unique HTTP basic authentication username and password and a unique self signed TLS certificate, and writes the credentials to a root only file. Unauthenticated requests are rejected, and the module protocol is reachable only over TLS with the per instance credentials.

What is included:

  • Athens 0.18.1 from the official upstream release binary, run under systemd (athens.service) as an unprivileged athens user with the proxy bound to 127.0.0.1:3000

  • The official Go 1.26.5 toolchain at /usr/local/go, used by Athens to fetch modules from the upstream Go mirror (falling back to direct VCS) on a cache miss

  • nginx (nginx.service) terminating TLS on port 443 as the only network facing service, fronting the whole proxy with HTTP basic auth, plus an unauthenticated /healthz for load balancer probes

  • A per instance HTTP basic authentication credential and TLS certificate, generated fresh on first boot, with the per instance values written to /root/athens-credentials.txt

  • The module cache on a dedicated data disk mounted at /var/lib/athens, separate from the operating system disk

Prerequisites

  • Active Azure subscription, SSH public key, VNet + subnet in target region

  • Subscription to the Athens listing on Azure Marketplace

  • Network Security Group rules allowing TCP 22 (admin) and TCP 443 (HTTPS, for the GOPROXY API) from the developers and build agents that use the proxy

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is ample for a team module proxy. Increase the size and enlarge the data disk when you cache a large module graph or serve many concurrent CI jobs.

Step 1: Deploy from the Azure Portal

Search Athens in Marketplace, select the cloudimg publisher, and click Create. Configure the Network Security Group to allow TCP 443 from the developer workstations and build agents that will use the proxy, and TCP 22 for administration. Keep 443 restricted to the networks you serve; a module proxy rarely needs to be reachable from the whole internet.

Step 2: Deploy from the Azure CLI

RG="athens-prod"; LOCATION="eastus"; VM_NAME="athens1"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/athens-go-proxy-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values "$SSH_KEY" \
  --public-ip-sku Standard
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 443 --priority 1001
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 22 --priority 1002

Step 3: First boot and per instance credentials

On first boot the image resolves this VM's IP, generates a per instance HTTP basic authentication username and password and a per instance TLS certificate, and writes everything to /root/athens-credentials.txt. This completes within seconds. SSH in as azureuser and read the credentials file:

sudo cat /root/athens-credentials.txt

The file is 0600 root:root and records the proxy user and password, the HTTPS proxy URL, and a ready to paste GOPROXY example. These values are unique to this instance.

Step 4: Confirm the services are running

athens.service and nginx.service are both active, athens -version reports 0.18.1, and ss confirms that nginx owns port 443 while the Athens proxy is bound to loopback only on port 3000.

systemctl is-active athens.service nginx.service
/opt/athens/athens -version
sudo ss -tlnp | grep -E ':443 |:3000 '

athens.service and nginx.service both report active, the athens binary reports version 0.18.1, and ss shows nginx listening on port 443 on all interfaces while the Athens proxy is bound to loopback 127.0.0.1 on port 3000 only

Step 5: The proxy is never open

The module proxy is reachable only through nginx on port 443, and only with the per instance HTTP basic authentication. An unauthenticated module request returns 401; a request with the per instance credentials returns 200. Only the /healthz load balancer probe is served without credentials. The block below reads the credentials from the instance credentials file, so it works unchanged on any instance.

PROXY_USER=$(sudo grep -E '^ATHENS_PROXY_USER=' /root/athens-credentials.txt | cut -d= -f2-)
PROXY_PASS=$(sudo grep -E '^ATHENS_PROXY_PASSWORD=' /root/athens-credentials.txt | cut -d= -f2-)
curl -sk -o /dev/null -w 'healthz    (no auth)   -> HTTP %{http_code}\n' https://127.0.0.1/healthz
curl -sk -o /dev/null -w 'list       (no auth)   -> HTTP %{http_code}\n' https://127.0.0.1/rsc.io/quote/@v/list
curl -sk -u "$PROXY_USER:$PROXY_PASS" -o /dev/null -w 'list       (with auth) -> HTTP %{http_code}\n' https://127.0.0.1/rsc.io/quote/@v/list

The -k flag tells curl to accept the self signed per instance certificate. The authenticated request returns the list of versions Athens can serve for the module, which it assembles from its own cache and the upstream mirror.

the module proxy returns HTTP 200 for the unauthenticated healthz probe, HTTP 401 for an unauthenticated module request, and HTTP 200 when the per instance basic auth credentials are supplied, and the authenticated list endpoint returns the available versions of the rsc.io/quote module

Step 6: Point the Go toolchain at the proxy

Set GOPROXY to the authenticated HTTPS URL of this proxy and run go mod download. The module is fetched through Athens, which caches it and serves it. The block below runs entirely on this VM against 127.0.0.1 to demonstrate the round trip; on a workstation you would use this VM's address instead (see Step 7).

PROXY_USER=$(sudo grep -E '^ATHENS_PROXY_USER=' /root/athens-credentials.txt | cut -d= -f2-)
PROXY_PASS=$(sudo grep -E '^ATHENS_PROXY_PASSWORD=' /root/athens-credentials.txt | cut -d= -f2-)
export PATH=/usr/local/go/bin:$PATH
export SSL_CERT_FILE=/etc/nginx/tls/athens.crt   # trust this VM's self signed certificate
export GOSUMDB=off
export GOPROXY="https://$PROXY_USER:$PROXY_PASS@127.0.0.1/"
export GOPATH=$(mktemp -d) GOMODCACHE=$(mktemp -d) HOME=$(mktemp -d)
cd "$(mktemp -d)"
go mod download -x github.com/pkg/errors@v0.9.1
echo "go mod download exit code: $?"

Every request in the trace is served with HTTP 200 through the proxy on 127.0.0.1, and go mod download exits 0. The module has now been fetched once and cached.

the go command is pointed at the Athens proxy with GOPROXY set to the authenticated HTTPS URL, and go mod download of github.com/pkg/errors fetches the module info, go.mod and zip through the proxy, each returning HTTP 200, then exits successfully

Step 7: The module cache on the dedicated data disk

Athens stores every fetched module in its on disk cache on the dedicated data disk mounted at /var/lib/athens, so the cache has room to grow and is separate from the operating system disk. Confirm the mount and inspect the cache:

df -h /var/lib/athens
sudo find /var/lib/athens/storage -maxdepth 3 -mindepth 1 -type d

The data disk is mounted at /var/lib/athens, and the cache lists every module Athens has fetched so far, each ready to be served to your whole team without touching the internet again.

df shows the dedicated data disk mounted at /var/lib/athens, and the storage directory lists the cached modules including github.com/pkg/errors and rsc.io/quote, each fetched once and now served from the local cache

Step 8: Use the proxy from your team and CI

On any developer workstation or CI agent, point the Go toolchain at this instance. Replace <vm-ip> with this instance's address (or a DNS name you point at it), and use the per instance credentials from /root/athens-credentials.txt.

# trust this instance's certificate (copy /etc/nginx/tls/athens.crt from the VM), then:
export SSL_CERT_FILE=/path/to/athens.crt
export GOPROXY="https://<user>:<password>@<vm-ip>/"
export GOSUMDB=off
go mod download
go build ./...

For CI, set the same GOPROXY, GOSUMDB and certificate trust as environment variables in your pipeline. Every job then pulls modules from the shared cache instead of the public internet, which is faster and keeps your builds reproducible.

Step 9: Use a real domain and certificate for production

The per instance certificate is self signed, so clients must trust it explicitly. For production, put a DNS name and a CA signed certificate in front of the proxy by replacing the per instance certificate in /etc/nginx/tls, so clients trust the transport with no extra configuration.

# replace the self signed certificate with your CA signed certificate + key, then reload nginx
sudo cp your-cert.crt /etc/nginx/tls/athens.crt
sudo cp your-cert.key /etc/nginx/tls/athens.key
sudo systemctl reload nginx
# point GOPROXY at https://<your-domain>/ with the per instance credentials

Step 10: Security recommendations

  • Restrict the NSG. Allow TCP 443 only from the developer subnets and build agents that use the proxy, and TCP 22 only from your administration network. A module proxy rarely needs to be public.

  • Protect the proxy password. It lives in /root/athens-credentials.txt (0600 root:root). The proxy is reachable only through nginx and only with this password; rotate it by updating /etc/nginx/athens.htpasswd with htpasswd and reloading nginx.

  • Use a real certificate for production. The per instance certificate is self signed. Put a CA signed certificate and a DNS name in front for production so clients trust the transport without extra configuration.

  • Size the data disk for your module graph. The cache grows as your team pulls more modules. Monitor /var/lib/athens and enlarge the data disk when needed.

  • Keep the OS patched. Unattended security upgrades remain enabled on the running VM.

Step 11: Support and Licensing

Athens is open source software distributed under the MIT License. This cloudimg image bundles the unmodified official Athens release binary and the official Go toolchain. cloudimg provides the packaging, the systemd hardening, the nginx TLS front, the loopback only proxy with per instance basic auth, the per instance credential automation, the dedicated data disk for the cache, the paired deploy guide, and 24/7 support with a guaranteed 24 hour response SLA.

cloudimg is not affiliated with or endorsed by the Athens project. Athens is a mark of its respective owner and is used here only to identify the software.

Deploy on Azure

Find Athens Go Module Proxy on Ubuntu 24.04 LTS on the Azure Marketplace, published by cloudimg. Deploy from the Portal or the Azure CLI as shown above.

Need Help?

Email support@cloudimg.co.uk for deployment help, configuration questions, or licensing enquiries.