Deployment guide

Athens Go proxy

Overview

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.

The result is 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. This SecureAnyCloud image deploys Athens Go Module Proxy on Ubuntu 24.04 on Azure, secured by default.

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.

The Athens server is bound to the loopback interface only (127.0.0.1:3000), and nginx is the only network-facing service. 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, with no shared credentials. Athens ships with no authentication of its own, so an unprotected Athens is an open proxy. This image never exposes it that way, and nothing secret is baked into it. 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 you get:

  • 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, which Athens uses 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 endpoint 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

  • An active Azure subscription, an SSH public key, and a VNet with a subnet in the target region
  • A subscription to the Athens listing on the Azure Marketplace
  • Network security group (NSG) rules that allow TCP 22 (administration) and TCP 443 (HTTPS, for the GOPROXY API) from the developers and build agents that use the proxy

Recommended VM 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.

1

Deploy from the Azure Portal

Search the Azure Marketplace for Athens, select the SecureAnyCloud publisher, and choose Create.

Configure the NSG to allow TCP 443 from the developer workstations and build agents that will use the proxy, and TCP 22 for administration. Restrict port 443 to the networks you serve, because a module proxy rarely needs to be reachable from the whole internet.

2

Deploy from the Azure CLI

RG="athens-prod"; LOCATION="eastus"; VM_NAME="athens1" IMAGE_URN="sac1647283583153:athens-go-proxy-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:athens-go-proxy-ubuntu-24-04:default:latest
az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$IMAGE_URN" \
  --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
3

First boot and per-instance credentials

On first boot, the image resolves this VM's IP address. It 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. It records the proxy user and password, the HTTPS proxy URL, and a ready-to-paste GOPROXY example. These values are unique to this instance.

4

Confirm the services are running

Run the commands below. Confirm that athens.service and nginx.service are both active and that athens -version reports 0.18.1. The ss output shows 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 '
5

Confirm 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.

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, use this VM's address instead (see Step 8).

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.

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. 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 output shows the data disk mounted at /var/lib/athens. The cache lists every module Athens has fetched so far, each ready to be served to your whole team without touching the internet again.

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.

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. Clients then 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
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. To rotate it, update /etc/nginx/athens.htpasswd with htpasswd and reload 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 of the proxy 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.
11

Support and Licensing

Athens is open-source software distributed under the MIT License. This SecureAnyCloud image bundles the unmodified official Athens release binary and the official Go toolchain.

SecureAnyCloud 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, this deployment guide, and 24/7 support with a guaranteed 24 hour response SLA.

SecureAnyCloud 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 SecureAnyCloud. Deploy from the Portal or the Azure CLI as shown above.

Need help?

Email sales@secureanycloud.com for deployment help, configuration questions, or licensing enquiries.

Deploy on Azure

Launch Athens Go Module Proxy 0.18.1 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure