Athens Go Module Proxy on Ubuntu 24.04 on Azure User Guide
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 unprivilegedathensuser with the proxy bound to127.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/healthzfor 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 '

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.

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.

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.

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.htpasswdwithhtpasswdand 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/athensand 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.