big-AGI 2.0.5
Overview
This guide covers launching and verifying big-AGI 2.0.5 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened Ollama, LiteLLM & Self Hosted LLM Server AMIs listing on AWS Marketplace. 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.
big-AGI is an open-source (MIT) AI chat and agent workspace. It gives you one web interface for the models you already use: OpenAI, Anthropic, Google Gemini, OpenRouter and more, or a self-hosted server such as Ollama, LocalAI or llama.cpp. It adds personas, side-by-side "Beam" answers from several models, voice calls, file and link attachments, diagram and code rendering, and chat import and export.
big-AGI is a chat client, not a model host. The models run at your providers or on your own inference server, so it needs no GPU. big-AGI has no server-side accounts and stores nothing on the server. Your provider keys, chats and personas live in your browser's local storage.
What you get:
- big-AGI 2.0.5, the upstream container image
ghcr.io/enricoros/big-agi:v2.0.5, pinned by digestsha256:539f8da1692222e35356e9499446405e99ad6c794ae264f5c989c038dbecfbc2, run by Docker CE (official Docker apt repository) with a restart policy - big-AGI bound to loopback only (
127.0.0.1:3000), with nginx as the only network-facing service - HTTPS on port 443 with a self-signed certificate created on your instance's first boot. Port 80 redirects to HTTPS.
- A per-instance login (HTTP Basic authentication, user
admin) generated on first boot into a root-only file big-agi-set-hostname, a helper that serves big-AGI under your own DNS name and re-issues the certificate for it
Default user: ubuntu | Architecture: x86_64 | Root volume: 12 GiB gp3
Security posture
- No default credential. The login password is generated on each instance's first boot and stored only as a bcrypt hash (
/etc/nginx/big-agi.htpasswd) and in a root-only file. It is never in the image, so no two instances share one. - Every page and API route needs the login. big-AGI itself has no authentication, so nginx enforces it in front of everything. That covers the UI and every
/apiroute, including the routes that call model providers. The only open path is nginx's own/healthzliteral, which never reaches big-AGI. - Fails closed. nginx will not start until first boot has finished and the per-instance password file exists.
- Only nginx faces the network. It answers only for the configured hostname, and closes the connection for any other
Hostheader. It uses TLS 1.2 or 1.3 and refuses cross-originPOSTrequests and WebSocket upgrades (403). It also strips CORS headers and replaces any client-suppliedX-Forwarded-For,X-Real-IPorForwardedheader with the real peer address. - The hostname comes from configuration, never from a request. First boot reads it from EC2 instance metadata (IMDSv2), or from
/etc/sac/big-agi-hostnameif you set one. - No instance metadata access for the app. big-AGI's server fetches whatever provider address a signed-in user enters. The container is therefore blocked from the instance metadata service (169.254.169.254 and fd00:ec2::254) by a Docker
DOCKER-USERfirewall rule, which is in place before the container starts. nginx is blocked from it by systemdIPAddressDeny. A provider address can never read the instance's role credentials. - IMDSv2 required. The AMI is registered with
ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests. - No third-party analytics. The upstream container image has its maintainer's Google Analytics ID built into the web app, so every browser session would report to a third party. This image removes that ID from the JavaScript that nginx serves, so big-AGI never loads Google Analytics. Next.js telemetry is also off.
- Per-instance identity. SSH host keys, the machine ID and Docker's engine ID are generated fresh on every instance.
Prerequisites
You need:
- An AWS account and permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
- An API key for at least one model provider (OpenAI, Anthropic, Gemini, OpenRouter and so on), or a self-hosted model server such as Ollama that the instance can reach
Recommended instance type: m5.large. big-AGI is a web client and holds no model, so 2 vCPUs and 8 GiB of memory are ample. The model work happens at your providers.
Connecting to your instance
Version Operating system SSH user
big-AGI 2.0.5 on Ubuntu 24.04 Ubuntu 24.04 LTS 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 Subscribe. One subscription covers every version in the listing.
- When 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 big-AGI 2.0.5 on Ubuntu 24.04, then choose your Region.
- Pick an instance type (
m5.largerecommended; the software charge is $0.04 per vCPU per hour) and your key pair. - Review the 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 your users browse from.
- Choose your VPC and subnet. Leave the metadata defaults, because 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 users' network, then launches the instance.
Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the VPC, subnet, key pair and CIDR ranges with your own:
SG=$(aws ec2 create-security-group --group-name big-agi \
--description "big-AGI chat" --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 203.0.113.0/24
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 80 --cidr 203.0.113.0/24
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=big-agi}]'You do not need --metadata-options: the AMI already makes IMDSv2 required.
Connect and find your login
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>First boot finishes within a minute of launch. It sets the hostname that big-AGI is served as (the instance's public DNS name when it has one) and creates the TLS certificate for it. It starts the big-AGI container on loopback, generates the login password, and writes /root/big-agi-credentials.txt (mode 0600, root). Then it starts nginx.
These are the entries in that file (the password is not shown here):
sudo grep -E '^BIG_AGI_(URL|USERNAME)=' /root/big-agi-credentials.txt
sudo grep -oE '^[A-Z_]+=' /root/big-agi-credentials.txt
sudo stat -c '%a %U:%G' /root/big-agi-credentials.txtBIG_AGI_URL=https://ec2-3-86-100-237.compute-1.amazonaws.com/ BIG_AGI_USERNAME=admin BIG_AGI_URL= BIG_AGI_USERNAME= BIG_AGI_PASSWORD= 600 root:rootValue What it is for
BIG_AGI_URL The address of this instance's big-AGI
BIG_AGI_USERNAME Login username (always admin)
BIG_AGI_PASSWORD Login password, generated on this instance
To read the password, run sudo cat /root/big-agi-credentials.txt in your own session. Store it in your password manager.
Confirm big-AGI is running
Check that the first-boot unit, Docker, the metadata firewall rule and nginx are active. Only sshd and nginx listen beyond loopback. big-AGI itself is on 127.0.0.1:3000, so the nginx login is the only way in.
systemctl is-active big-agi-firstboot.service docker.service sac-docker-imds-deny.service nginx.service
sudo docker ps --format '{{.Names}} {{.Ports}}'
sudo ss -Htlnp | awk '{print $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort -u curl -sk https://127.0.0.1/healthzactive active active active big-agi 127.0.0.1:3000->3000/tcp 0.0.0.0:22 users:(("sshd"),("systemd")) 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) 127.0.0.1:3000 users:(("docker-proxy")) 127.0.0.53%lo:53 users:(("systemd-resolve")) 127.0.0.54:53 users:(("systemd-resolve")) [::]:22 users:(("sshd"),("systemd")) [::]:443 users:(("nginx"),("nginx"),("nginx")) [::]:80 users:(("nginx"),("nginx"),("nginx")) ok/healthz is answered by nginx itself and reveals nothing, so it is safe to use as a load balancer health check.
Prove the login and the web defaults
Anonymous requests to the UI and to the API are refused, and so is a password that is wrong by one character. This instance's own password is accepted. A cross-origin POST is refused even with the right password. An unknown Host header gets no answer, and plain HTTP redirects to the configured HTTPS name:
H=$(sudo sed -n 's/^set \$app_host "\(.*\)";$/\1/p' /etc/nginx/big-agi-host.conf)
P=$(sudo grep '^BIG_AGI_PASSWORD=' /root/big-agi-credentials.txt | cut -d= -f2-)
curl -sk -o /dev/null -w 'no password -> %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/"
curl -sk -o /dev/null -w 'no password, API -> %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/api/edge/llmOllama.listModels"
curl -sk -o /dev/null -w 'wrong password -> %{http_code}\n' -K <(printf 'user = "admin:%sX"\n' "$P") --resolve "$H:443:127.0.0.1" "https://$H/"
curl -sk -o /dev/null -w 'this instance password -> %{http_code}\n' -K <(printf 'user = "admin:%s"\n' "$P") --resolve "$H:443:127.0.0.1" "https://$H/"
curl -sk -o /dev/null -w 'cross-origin POST -> %{http_code}\n' -K <(printf 'user = "admin:%s"\n' "$P") --resolve "$H:443:127.0.0.1" \
-X POST -H 'Origin: https://evil.example' -H 'Content-Type: application/json' -d '{}' "https://$H/api/edge/x"
curl -sk -o /dev/null -w 'unknown Host -> %{http_code}\n' -H 'Host: evil.example' https://127.0.0.1/ || true
curl -s -o /dev/null -w 'http -> %{http_code} %{redirect_url}\n' -H "Host: $H" http://127.0.0.1/no password -> 401 no password, API -> 401 wrong password -> 401 this instance password -> 200 cross-origin POST -> 403 unknown Host -> 000 http -> 301 https://ec2-3-86-100-237.compute-1.amazonaws.com/The password is passed to curl through a config file on a file descriptor, so it never appears on a command line or in your shell history.
Sign in to big-AGI
Browse to BIG_AGI_URL (https://<public-dns-name>/). The certificate is self-signed, so your browser warns once. Accept it for this host, or install your own certificate (Step 8).
The browser then asks for a username and password. Enter admin and BIG_AGI_PASSWORD.
On your first visit, big-AGI shows a short notice that you bring your own API credentials. Choose I understand to open Setup AI Models.
Connect your model providers
big-AGI has no provider configured, and no key ships with the image. In Setup AI Models, paste the API key for any of the popular providers (OpenAI, Anthropic, Gemini, OpenRouter), or choose More Services for the full list. Everything you enter is saved in your browser's local storage, not on the instance:
To use a self-hosted model server, switch to Local and enter the server's address. For example, use the private address of an instance running the Ollama version of this listing (http://<private-ip>:11434).
The address must be reachable from the instance, because big-AGI's server makes the call. Inside the big-AGI container, 127.0.0.1 is the container itself, not the instance. Make sure the model server's security group allows port 11434 from this instance.
Start chatting
Choose skip (or Done once you have entered a key) to reach the chat home. Pick a persona and type in the composer. Press Chat, or press Beam to ask several models at once and merge their answers:
The Apps & Tools button in the left rail opens Create Personas, Compare Text, News, the AI Inspector and Clipboard History:
Your chats are kept in the browser you use. Use Export in the chat drawer to back them up, and Import to move them to another browser.
Serve big-AGI under your own DNS name
Point a DNS record at the instance (an Elastic IP keeps the address stable), then tell big-AGI the name. The helper re-issues the self-signed certificate for it and updates nginx's accepted names and the cross-origin guard. It also records the name in /etc/sac/big-agi-hostname (kept across reboots) and updates BIG_AGI_URL:
sudo big-agi-set-hostname chat.example.combig-agi-set-hostname: big-AGI is served at https://chat.example.com/To use a certificate from your own CA or from Let's Encrypt instead, replace /etc/ssl/big-agi/big-agi.crt and /etc/ssl/big-agi/big-agi.key (keep the key mode 0600, owner root) and run sudo systemctl reload nginx.
Check the security controls
The certificate was made for this instance on first boot. The container's traffic to the instance metadata service is dropped, and nginx is denied it too. The last line runs a request to the metadata service from inside the big-AGI container:
H=$(sudo sed -n 's/^set \$app_host "\(.*\)";$/\1/p' /etc/nginx/big-agi-host.conf)
openssl s_client -connect 127.0.0.1:443 -servername "$H" </dev/null 2>/dev/null | openssl x509 -noout -subject -enddate sudo iptables -S DOCKER-USER | grep DROP systemctl show -p IPAddressDeny --value nginx.service
sudo docker exec big-agi node -e 'fetch("http://169.254.169.254/latest/api/token",{method:"PUT",headers:{"X-aws-ec2-metadata-token-ttl-seconds":"60"},signal:AbortSignal.timeout(5000)}).then(r=>console.log("reached: HTTP "+r.status)).catch(e=>console.log("blocked: "+((e.cause&&e.cause.code)||e.name)))'subject=CN = ec2-3-86-100-237.compute-1.amazonaws.com, O = sac notAfter=Jan 21 23:04:10 2029 GMT -A DOCKER-USER -d 169.254.169.254/32 -j DROP -A DOCKER-USER -d 168.63.129.16/32 -j DROP 169.254.169.254/32 168.63.129.16/32 blocked: TimeoutErrorThe running container is the pinned release, telemetry is off, and the Google Analytics ID built into the upstream image is not served to browsers (the count is 0):
sudo docker image inspect --format '{{range .RepoDigests}}{{println .}}{{end}}' "$(sudo docker inspect --format '{{.Image}}' big-agi)"
sudo docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' big-agi | grep TELEMETRY
H=$(sudo sed -n 's/^set \$app_host "\(.*\)";$/\1/p' /etc/nginx/big-agi-host.conf)
P=$(sudo grep '^BIG_AGI_PASSWORD=' /root/big-agi-credentials.txt | cut -d= -f2-)
APP=$(curl -sk -K <(printf 'user = "admin:%s"\n' "$P") --resolve "$H:443:127.0.0.1" "https://$H/" | grep -oE '/_next/static/chunks/pages/_app-[A-Za-z0-9_-]+\.js' | head -1) curl -sk --compressed -K <(printf 'user = "admin:%s"\n' "$P") --resolve "$H:443:127.0.0.1" "https://$H$APP" | grep -c 'G-D4J355ELL1' || trueghcr.io/enricoros/big-agi@sha256:539f8da1692222e35356e9499446405e99ad6c794ae264f5c989c038dbecfbc2 NEXT_TELEMETRY_DISABLED=1 0Change the login password
The password is read from standard input, so it never appears on a command line, in your shell history or in the system logs. Enter the new password when prompted, and then reload nginx:
sudo htpasswd -i -B /etc/nginx/big-agi.htpasswd admin
sudo systemctl reload nginxThen put the new value in /root/big-agi-credentials.txt (or delete that file) so it does not hold a stale password.
Maintenance
- Logs. Use
sudo docker logs big-agifor big-AGI,/var/log/nginx/for HTTPS access, andsudo journalctl -u big-agi-firstboot.servicefor first boot. - Restart. Run
sudo docker restart big-agi, orsudo systemctl restart nginx. - Operating system updates. Ubuntu's unattended security upgrades stay on, so nginx, Docker and the rest of the OS keep receiving patches. Apply the rest with
sudo apt update && sudo apt upgradein a maintenance window. - Upgrading big-AGI. The container image is pinned by digest and first boot never pulls. To upgrade deliberately, pull a newer release by digest and remove the
big-agicontainer. Then run the new image with the same options that the first-boot script uses (/usr/local/sbin/big-agi-firstboot.sh):--restart unless-stopped,-e NEXT_TELEMETRY_DISABLED=1and-p 127.0.0.1:3000:3000. Never publish the port on all interfaces, and never use host networking, because that would bypass both the login and the metadata firewall rule. A newer release may build in a different analytics ID. Check it with the last command of Step 9. - Your data. Nothing is stored on the server. Chats, personas and provider keys are in your browser. Export them from the chat drawer to back them up.
Troubleshooting
The browser keeps asking for a password. Use admin and BIG_AGI_PASSWORD from /root/big-agi-credentials.txt exactly, over https://.
nginx will not start. It waits for first boot to finish and for the password file to exist, by design. Check first boot with sudo journalctl -u big-agi-firstboot.service --no-pager | tail -20.
A model server is "not reachable". The address is called from the instance, not from your browser. Use an address that the instance can reach (a private IP in the same VPC, or a public endpoint), and allow the port in that server's security group. Instance metadata addresses are blocked on purpose.
Support
SecureAnyCloud provides 24/7 support for this image by email and live chat: launching and connecting, provider setup, TLS and hostnames, and upgrades.
big-AGI is developed by Enrico Ros and contributors and is released under the MIT license. Ubuntu is a registered trademark of Canonical Ltd. Docker is a trademark of Docker, Inc. All other product and company names are trademarks or registered trademarks of their respective holders. SecureAnyCloud is not affiliated with or endorsed by them.
Deploy on AWS
Launch big-AGI 2.0.5 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