Ollama 0.40.6
Overview
Ollama is an open-source (MIT) server for running open-weights large language models. This SecureAnyCloud image runs Ollama 0.40.6 on Ubuntu 24.04 on Amazon EC2. It is one version of the SecureAnyCloud Hardened Ollama, LiteLLM & Self-Hosted LLM Server AMIs listing on AWS Marketplace, and you pick it as the software version when you subscribe and launch. The other LLM servers, gateways and chat interfaces in the listing are described on the listing's guide hub.
Ollama has its own REST API (/api/generate, /api/chat, /api/embed, /api/tags, /api/pull and more) and an OpenAI-compatible API (/v1/chat/completions, /v1/completions, /v1/embeddings, /v1/models), so code written for the OpenAI SDK, LangChain or LlamaIndex can use it with a base URL and a credential. Prompts and answers stay inside your VPC.
This image runs Ollama on the CPU, so it needs no GPU. The Qwen2.5 1.5B Instruct model (qwen2.5:1.5b, Apache 2.0) is pulled at build time and ships on the image, so the API answers as soon as the instance is up. Pull larger models for real work (Step 9).
What you get:
- Ollama 0.40.6 at /usr/local/bin/ollama, rebuilt by SecureAnyCloud from the upstream v0.40.6 source with Go 1.26.10 and patched Go modules, and run by systemd as the unprivileged ollama user (the build record is in /usr/share/doc/sac/ollama/)
- Upstream's 0.40.6 CPU runtime libraries in
/usr/local/lib/ollama(the llama.cpp backends for every x86-64 CPU generation, with their licence texts), taken from the checksum-verified release archive; the CUDA, ROCm and Vulkan GPU runtimes are not installed - The
qwen2.5:1.5bmodel in/var/lib/ollama/models, pinned to its published manifest and file digests, with its licence in/usr/share/doc/sac/ollama-models/ - Ollama bound to loopback only (
127.0.0.1:11434) and nginx as the only network-facing service - HTTPS on port 443 with a self-signed certificate created on your instance's first boot, and port 80 redirecting to HTTPS
- A per-instance login (HTTP Basic, user
admin) on every API path, generated on first boot into a root-only file ollama-set-hostname, a helper that serves Ollama under your own DNS name and re-issues the certificate for it
Default user: ubuntu
Architecture: x86_64
Root volume: 30 GiB gp3 (models live in /var/lib/ollama/models)
Security posture
- No default credential. The login password is generated on each instance's first boot and stored only as a bcrypt hash in nginx and in a root-only file. It is not in the image, so no two instances share one. Ollama's own identity key (
/var/lib/ollama/.ollama/id_ed25519) is also created per instance. - Every path needs the credential. Ollama has no authentication of its own, so it listens on loopback only and nginx asks for the login on every path except the version check
GET /api/versionand nginx'sGET /healthz. A wrong password is refused, and the client'sAuthorizationheader is never passed to Ollama. - Cloud features are off.
OLLAMA_NO_CLOUD=1turns off Ollama's cloud models, sign-in and the web search and web fetch proxies, so the instance only talks to the model registry when you pull a model. - No instance metadata for Ollama. The
ollamaservice, every model runner it starts, and nginx are denied the instance metadata service by systemdIPAddressDeny, so no pull or create request can make Ollama read the instance's role credentials, even one sent with"insecure": true. - Registry redirects are checked. Ollama 0.40.6 refuses a registry redirect that downgrades HTTPS to HTTP or points a pull at a different, non-public host; older releases (before 0.34.2) followed such redirects to any address.
- Fails closed. nginx will not start until first boot has created the certificate and the credential, and first boot stops nginx again if its own check of the credential gate fails.
- Only nginx faces the network. It answers only for the configured hostname (any other
Hostheader gets its connection closed), uses TLS 1.2 or 1.3, refuses cross-originPOSTrequests and WebSocket upgrades (403), strips CORS headers, and replaces any client-suppliedX-Forwarded-FororX-Real-IPheader with the real peer address.OLLAMA_ORIGINSis not set. - The hostname comes from configuration, never from a request. First boot reads it from EC2 instance metadata (IMDSv2), or from
/etc/sac/ollama-hostnameif you set one. - Patched build. Every upstream Ollama release up to 0.40.6 is built with Go 1.26.0 and carries 84 known Go advisories (in net/http, crypto/tls, crypto/x509, golang.org/x/net and others). The SecureAnyCloud binary is the same 0.40.6 source built with Go 1.26.10 and the fixed module releases. One advisory remains: GO-2026-5932 for the deprecated golang.org/x/crypto/openpgp package, which has no fixed release and which Ollama does not use (Step 10 shows the record).
- IMDSv2 required. The AMI is registered with
ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests. - Per-instance identity. SSH host keys and the machine ID are generated fresh on every instance.
Prerequisites
- An AWS account and permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
Recommended instance type: m5.large (2 vCPU, 8 GiB) for the bundled 1.5B model. Ollama runs models on the CPU and keeps them in memory, and generation speed scales with vCPUs and memory bandwidth. As a measured reference, on 4 vCPUs (Intel Xeon Platinum 8259CL) the 1.5B model generates about 8 tokens per second and reads prompts at about 60 tokens per second; expect roughly half that on m5.large. For 3B to 8B models choose m5.xlarge (16 GiB) or larger, and for interactive use of 7B to 8B models a compute-optimised type such asc5.4xlarge or c6i.4xlarge. The software charge is per vCPU.
Connecting to your instance
- Version: Ollama 0.40.6 on Ubuntu 24.04
- Operating system: Ubuntu 24.04 LTS
- SSH user: ubuntu
Launch the instance
Option A: AWS Marketplace console
- Open the SecureAnyCloud Hardened Ollama, LiteLLM & Self-Hosted LLM Server AMIs listing on AWS Marketplace, choose View purchase options, then choose Subscribe. One subscription covers every version in the listing.
- Once the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose Ollama 0.40.6 on Ubuntu 24.04, then choose your Region.
- Pick an instance type (
m5.largefor the starter model, or a larger type for real models) and your key pair. Increase the root volume if you plan to pull several large models. - Security group: the listing's default group opens SSH (22), HTTP (80) and HTTPS (443). Restrict SSH to your own IP address, and restrict 80 and 443 to the networks that your users and applications connect from.
- Choose your VPC and subnet, leave the metadata defaults (the image already requires IMDSv2), then choose Launch.
Option B: AWS CLI
Run this on your own workstation. It creates a security group that allows SSH from your management address and HTTPS from your application network, then launches the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and the VPC, subnet, key pair and CIDR ranges with your own:
SG=$(aws ec2 create-security-group --group-name ollama \
--description "Ollama LLM API" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr 10.0.0.0/16
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 80 --cidr 10.0.0.0/16
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--subnet-id subnet-0123456789abcdef0 \
--security-group-ids "$SG" \
--block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=60,VolumeType=gp3}' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=ollama}]'First boot takes about a minute after the instance is running: it creates the certificate and the login, starts Ollama and nginx, and checks the login gate before it finishes.
Connect and find your credentials
Connect with SSH as ubuntu:
ssh -i my-key-pair.pem ubuntu@<public-dns-name>The login is in a root-only file. These commands print the hostname, URL and user name, list the keys in the file without their values, and show its permissions:
sudo grep -E '^OLLAMA_(HOST|URL|USERNAME)=' /root/ollama-credentials.txt
sudo grep -oE '^OLLAMA_[A-Z]+=' /root/ollama-credentials.txt
sudo stat -c '%a %U:%G' /root/ollama-credentials.txtOLLAMA_HOST=ec2-3-82-98-249.compute-1.amazonaws.com OLLAMA_URL=https://ec2-3-82-98-249.compute-1.amazonaws.com/ OLLAMA_USERNAME=admin OLLAMA_HOST= OLLAMA_URL= OLLAMA_USERNAME= OLLAMA_PASSWORD= 600 root:rootTo read the password, run sudo cat /root/ollama-credentials.txt in your own session and store it in your secrets manager. The self-signed certificate your clients need to trust is /etc/ssl/ollama/ollama.crt.
Confirm Ollama is running
Ollama listens on loopback only; nginx serves ports 80 and 443. The version check is open:
systemctl is-active ollama.service nginx.service ollama --version sudo ss -Htlnp | grep -E ':(22|80|443|11434) ' | awk '{split($6,a,"\""); print $4, a[2]}' | sort -u
H=$(sudo grep '^OLLAMA_HOST=' /root/ollama-credentials.txt | cut -d= -f2-)
curl -s --cacert /etc/ssl/ollama/ollama.crt --resolve "$H:443:127.0.0.1" "https://$H/api/version"; echoactive active ollama version is 0.40.6 0.0.0.0:22 sshd 0.0.0.0:443 nginx 0.0.0.0:80 nginx 127.0.0.1:11434 ollama [::]:22 sshd [::]:443 nginx [::]:80 nginx {"version":"0.40.6"}An EBS volume created from an AMI fetches each block from Amazon S3 the first time it is read, so loading a model for the first time on a new instance is slow (we measured one to over three minutes for the 1 GB starter model). First boot therefore reads the shipped model once in the background (ollama-model-prewarm.service, at idle I/O priority). On a t3.large this took about three and a half minutes. A request sent before it finishes still works, but it waits for the read. This command waits up to about three minutes for the read to finish and shows how long it took:
for i in $(seq 1 34); do [ -f /var/lib/sac/data/ollama-prewarm.done ] && break; sleep 5; done cat /var/lib/sac/data/ollama-prewarm.done 2>/dev/null || echo "model pre-read still running"bytes=986062749 seconds=212Models you pull later are written to the volume directly, so they load at full speed.
Prove the credential gate
Requests without the login are refused on every API path, and so is a wrong password. An unknown Host header gets no answer (curl reports 000), and plain HTTP redirects to the configured HTTPS name:
H=$(sudo grep '^OLLAMA_HOST=' /root/ollama-credentials.txt | cut -d= -f2-) C=(-s -o /dev/null --cacert /etc/ssl/ollama/ollama.crt --resolve "$H:443:127.0.0.1") for p in /api/tags /api/ps /v1/models; do echo "anonymous $p -> $(curl "${C[@]}" -w '%{http_code}' "https://$H$p")"; done echo "wrong password -> $(curl "${C[@]}" -w '%{http_code}' -u admin:wrong-password "https://$H/api/tags")" echo "unknown Host -> $(curl -sk -o /dev/null -w '%{http_code}' -H 'Host: evil.example' https://127.0.0.1/api/version || true)" curl -s -o /dev/null -w 'plain HTTP -> %{http_code} %{redirect_url}\n' -H "Host: $H" http://127.0.0.1/api/tagsanonymous /api/tags -> 401 anonymous /api/ps -> 401 anonymous /v1/models -> 401 wrong password -> 401 unknown Host -> 000 plain HTTP -> 301 https://ec2-3-82-98-249.compute-1.amazonaws.com/api/tagsList the installed models
H=$(sudo grep '^OLLAMA_HOST=' /root/ollama-credentials.txt | cut -d= -f2-)
PW=$(sudo grep '^OLLAMA_PASSWORD=' /root/ollama-credentials.txt | cut -d= -f2-)
curl -s --cacert /etc/ssl/ollama/ollama.crt --resolve "$H:443:127.0.0.1" -u "admin:$PW" "https://$H/api/tags" \
| jq '.models[] | {name, size, digest: .digest[0:12], details: {family: .details.family, parameter_size: .details.parameter_size, quantization_level: .details.quantization_level}}'{ "name": "qwen2.5:1.5b", "size": 986061892, "digest": "65ec06548149", "details": { "family": "qwen2", "parameter_size": "1.5B", "quantization_level": "Q4_K_M" } }The digest is the published manifest of qwen2.5:1.5b in the Ollama library; the image build refuses any other.
Generate text
With Ollama's native API:
H=$(sudo grep '^OLLAMA_HOST=' /root/ollama-credentials.txt | cut -d= -f2-)
PW=$(sudo grep '^OLLAMA_PASSWORD=' /root/ollama-credentials.txt | cut -d= -f2-)
curl -s --cacert /etc/ssl/ollama/ollama.crt --resolve "$H:443:127.0.0.1" -u "admin:$PW" "https://$H/api/generate" \
-d '{"model":"qwen2.5:1.5b","prompt":"In one sentence, what is a reverse proxy?","stream":false,"options":{"seed":42,"temperature":0}}' \
| jq '{response, eval_count, tokens_per_second: (.eval_count / (.eval_duration / 1e9) | floor)}'{ "response": "A reverse proxy is a server that acts as an intermediary between clients and other servers, managing requests and routing them to the appropriate destination.", "eval_count": 28, "tokens_per_second": 9 }With the OpenAI-compatible API:
H=$(sudo grep '^OLLAMA_HOST=' /root/ollama-credentials.txt | cut -d= -f2-)
PW=$(sudo grep '^OLLAMA_PASSWORD=' /root/ollama-credentials.txt | cut -d= -f2-)
curl -s --cacert /etc/ssl/ollama/ollama.crt --resolve "$H:443:127.0.0.1" -u "admin:$PW" "https://$H/v1/chat/completions" \
-H 'Content-Type: application/json' \
-d '{"model":"qwen2.5:1.5b","messages":[{"role":"user","content":"Name three planets. Answer with a comma separated list only."}],"temperature":0,"seed":42}' \
| jq '{model, content: .choices[0].message.content, usage}'{ "model": "qwen2.5:1.5b", "content": "Mercury, Venus, Earth", "usage": { "prompt_tokens": 41, "prompt_tokens_details": { "cached_tokens": 24 }, "completion_tokens": 7, "total_tokens": 48 } }The tokens_per_second figure is from a 4 vCPU instance; your figure depends on the instance type (see Prerequisites).
Call Ollama from your application
Copy /etc/ssl/ollama/ollama.crt to your client, or replace it with a certificate from your own CA (Step 8). Send the login with every request. With the OpenAI Python SDK:
import httpx from openai import OpenAI client = OpenAI( base_url="https://ollama.example.com/v1", api_key="unused",
# Ollama ignores it; nginx checks the Basic login below http_client=httpx.Client(verify="ollama.crt", auth=("admin", "<OLLAMA_PASSWORD>")), ) reply = client.chat.completions.create( model="qwen2.5:1.5b", messages=[{"role": "user", "content": "Summarise our VPN policy in two lines."}], ) print(reply.choices[0].message.content)Ollama's own client libraries and tools that speak the Ollama API (OLLAMA_HOST=https://ollama.example.com) work the same way when they can send HTTP Basic credentials. Keep stream on for chat front ends, because nginx passes streamed responses through without buffering.
Serve Ollama under your own DNS name
Point a DNS record at the instance, then run:
sudo ollama-set-hostname ollama.example.comollama-set-hostname: Ollama is served at https://ollama.example.com/This re-issues the self-signed certificate for the new name (keeping the instance's own addresses in it), makes nginx answer for it and updates the credentials file. To use a certificate from your own CA or from Let's Encrypt instead, replace /etc/ssl/ollama/ollama.crt and /etc/ssl/ollama/ollama.key (keep the key mode 0600, owner root) and run sudo systemctl reload nginx.
Add models
Pull models from the Ollama library through the API (any authenticated client) or with the ollama command on the instance, which talks to the local server:
ollama pull qwen2.5:7b ollama listOr remotely:
curl --cacert ollama.crt -u "admin:<OLLAMA_PASSWORD>" https://ollama.example.com/api/pull -d '{"model":"qwen2.5:7b"}'Check each model's licence before you use it (ollama show <model> --license), because many open-weights models carry their own terms. A 4-bit quantised model needs roughly 0.6 GB of memory per billion parameters plus the context. Its files go to /var/lib/ollama/models on the root volume. If you need more space, grow the volume in EC2 and run sudo growpart /dev/nvme0n1 1 && sudo resize2fs /dev/nvme0n1p1. Remove a model with ollama rm <model>.
A pull with "insecure": true (or ollama pull --insecure) is for your own private registry: it allows plain HTTP and turns off Ollama's private-address checks for that pull. Instance metadata stays blocked either way. If your Ollama should never reach your private networks at all, add a systemd drop-in that denies them:
sudo tee /etc/systemd/system/ollama.service.d/60-private-deny.conf >/dev/null <<'EOF' [Service] IPAddressDeny=10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 100.64.0.0/10 fd00::/8 EOF
sudo systemctl daemon-reload && sudo systemctl restart ollama.serviceCheck the security controls
The cloud proxies are switched off, a pull aimed at the instance metadata address dies at the connection (systemd drops the packets), and the metadata service answers your shell but not the Ollama service:
H=$(sudo grep '^OLLAMA_HOST=' /root/ollama-credentials.txt | cut -d= -f2-)
PW=$(sudo grep '^OLLAMA_PASSWORD=' /root/ollama-credentials.txt | cut -d= -f2-) C=(-s -o /dev/null --cacert /etc/ssl/ollama/ollama.crt --resolve "$H:443:127.0.0.1" -u "admin:$PW") for p in /api/experimental/web_fetch /api/experimental/web_search; do echo "POST $p -> $(curl "${C[@]}" -w '%{http_code}' -X POST -d '{"url":"http://169.254.169.254/latest/meta-data/","query":"x"}' "https://$H$p")" done curl -s -m 60 --cacert /etc/ssl/ollama/ollama.crt --resolve "$H:443:127.0.0.1" -u "admin:$PW" "https://$H/api/pull" \
-d '{"model":"169.254.169.254/library/probe:latest","insecure":true,"stream":false}'; echo systemctl show -p IPAddressDeny --value ollama.service
CG=$(systemctl show -p ControlGroup --value ollama.service)
curl -s -m 5 -o /dev/null -w 'metadata from your shell -> %{http_code}\n' -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token
sudo bash -c "echo \$\$ > /sys/fs/cgroup$CG/cgroup.procs; exec curl -s -m 5 -o /dev/null -w 'metadata from inside ollama.service -> %{http_code}\n' -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token" || truePOST /api/experimental/web_fetch -> 403 POST /api/experimental/web_search -> 403 {"error":"pull model manifest: Get \"http://169.254.169.254/v2/library/probe/manifests/latest\": dial tcp 169.254.169.254:80: i/o timeout"} 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 metadata from your shell -> 200 metadata from inside ollama.service -> 000The build record names the exact source, toolchain and module versions of the binary you are running:
cat /usr/share/doc/sac/ollama/BUILD-RECORD.txt sha256sum /usr/local/bin/ollamasac d-102 dependency rebuild of the ollama binary source https://github.com/ollama/ollama/archive/refs/tags/v0.40.6.tar.gz source sha256 5adad63f326e5a3911d5248e923ac03def72b6ba5ba80fe1c6089cf26b9291c3 toolchain go1.26.10 linux/amd64, official tarball https://go.dev/dl/go1.26.10.linux-amd64.tar.gz toolchain sha256 ccfe7ae822d2c2f41f4fe7c956199967a4dac22d0ee0d2b2bfdb440aab3b20c5 cgo CGO_ENABLED=1 CGO_CFLAGS=-O3 CGO_CXXFLAGS=-O3, gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 build go build -trimpath -buildmode=pie -ldflags "-w -s -X version.Version=0.40.6 -X server.mode=release" bumps golang.org/x/net v0.46.0 v0.60.0; golang.org/x/crypto v0.43.0 v0.57.0; golang.org/x/image v0.22.0 v0.45.0; golang.org/x/text v0.30.0 v0.42.0; golang.org/x/mod v0.30.0 v0.41.0; github.com/klauspost/compress v1.18.3 v1.18.7; github.com/buger/jsonparser v1.1.1 v1.1.2; transitive golang.org/x/sync golang.org/x/sys golang.org/x/term govulncheck v1.9.0 -mode=binary: GO-2026-5932 binary sha256 75ae92ec4a5748ff9c1ffc163086f77626aab4514f2efb19396dd893163b52a3 75ae92ec4a5748ff9c1ffc163086f77626aab4514f2efb19396dd893163b52a3 /usr/local/bin/ollamaThe full govulncheck report is next to it in govulncheck.txt. The remaining GO-2026-5932 covers the deprecated golang.org/x/crypto/openpgp package, which has no fixed release; Ollama does not import it (the binary is stripped, so govulncheck reports the whole module).
Rotate the login password
Generate a new password, write its bcrypt hash for nginx (the password goes in on standard input, never on a command line), update the credentials file and reload nginx:
NEW=$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | cut -c1-24) printf '%s\n' "$NEW" | sudo htpasswd -iB /etc/nginx/.ollama.htpasswd admin
sudo sed -i "s/^OLLAMA_PASSWORD=.*/OLLAMA_PASSWORD=$NEW/" /root/ollama-credentials.txt
sudo systemctl reload nginx unset NEWMaintenance
- Operating system updates: unattended-upgrades applies Ubuntu security updates daily. Run
sudo apt update && sudo apt full-upgradefor everything else and reboot when a new kernel is installed. - Ollama updates: the image runs exactly 0.40.6 and does not update itself. SecureAnyCloud publishes new versions of this listing as Ollama releases. To move to one, launch the new version and copy /var/lib/ollama/models across, or pull your models again.
- Logs:
journalctl -u ollama.service,journalctl -u ollama-firstboot.serviceand/var/log/nginx/. - Ollama settings:
sudo systemctl edit ollama.servicelets you set Ollama's own variables, for exampleEnvironment=OLLAMA_KEEP_ALIVE=30m(how long a model stays loaded) orEnvironment=OLLAMA_CONTEXT_LENGTH=8192.
Troubleshooting
401with the right password: check that you send it as HTTP Basic for useradminand that the client sends it to the configured hostname (sudo grep ^OLLAMA_HOST= /root/ollama-credentials.txt).- Connection closed with no response: the request's
Hostdoes not match a configured name. Use the instance's public DNS name or IP, or set your own withollama-set-hostname. - Certificate errors: trust
/etc/ssl/ollama/ollama.crton the client, or install your own certificate (Step 8). - nginx is not running after boot: first boot did not finish. See
journalctl -u ollama-firstboot.service. Remove/var/lib/sac/data/ollama-firstboot.doneand runsudo systemctl restart ollama-firstboot.serviceto retry. - Slow or failed generation: the model may not fit in memory (
free -m,journalctl -u ollama.service); use a smaller model or a larger instance type.
Support
SecureAnyCloud provides support for this image by email at sales@secureanycloud.com. Include the instance ID, the Region and the version you launched.
Deploy on AWS
Launch Ollama 0.40.6 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com