Fossil 2.28
Overview
Fossil is a simple, high-reliability, distributed version control system from the creator of SQLite. The SecureAnyCloud Fossil 2.28 on Ubuntu 24.04 AMI runs it on Amazon EC2 behind nginx, served over HTTPS, with a private repository and an administrator password that is unique to your instance. It is one version of the SecureAnyCloud Apache Subversion (SVN) and CI/CD Tools listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.
One self-contained program gives a project distributed source control, a web interface with a timeline of every change, a wiki, a ticket-based bug tracker, a forum and a chat room. A whole repository, including its wiki and tickets, is one SQLite file. There is nothing else to install, and backing it up is copying one file.
What you get:
- HTTPS from the first boot. nginx serves Fossil on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS, and Fossil marks its login cookie Secure.
- Your own repository and admin password. The image ships with no repository. First boot creates a new, empty one and an
adminpassword unique to your instance, written to/root/fossil-info.txt. The password is handed to Fossil on standard input, never on a command line. - Private by default. Visitors who have not signed in get nothing: no timeline, no files, no wiki, no clone and no download. You decide later whether anything should be public (Step 9).
- Fail closed. Fossil refuses to start until first boot has finished, and so does nginx.
- Loopback only. Fossil listens on
127.0.0.1:8081behind nginx, which rate limits requests. The only open ports are 22, 80 and 443. - Hardened defaults. Fossil runs as its own unprivileged user in a systemd sandbox that can write only
/var/lib/fossil. Fossil and nginx are blocked from the instance metadata service, and the AMI requires IMDSv2. The instance drops requests for a host name it does not serve and refuses cross-site requests that would change data. Fossil builds its links from the configured host name, never from what a client sends. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: Fossil 2.28 (the official static Linux x64 build, checksum verified), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu; Architecture: x86_64; Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet with a route to the internet (or a private route that you use for management).
- A security group that allows TCP 22 (SSH), TCP 443 (the web interface and clone, push and pull) and TCP 80 (redirects to HTTPS) from your own networks only.
- Fossil 2.x on each workstation that will clone the repository (
apt install fossil,brew install fossil, or a download from fossil-scm.org).
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Fossil 2.28 on Ubuntu 24.04 as the software version and select your Region, then choose Launch through EC2.
Choose m5.large, your key pair and a security group as described above. Fossil is light, so a smaller instance is fine for a small team. The repository lives under /var/lib/fossil on the root volume, so size the root volume for your project's history.
Alternatively, launch with the AWS CLI. Replace the values in angle brackets:
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=fossil}]'Connect to your instance
Connect over SSH as the ubuntu user, which is the SSH user for Fossil 2.28 on Ubuntu 24.04:
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot takes a few seconds. After that, all three units are active:
systemctl is-active fossil nginx fossil-firstbootactive active activeFossil listens on loopback only, and nginx serves ports 80 and 443. Confirm the listeners and the Fossil version:
sudo ss -tlnp | awk 'NR==1 || /fossil|nginx|sshd/' | awk '{print $1, $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' fossil versionState Local Peer LISTEN 127.0.0.1:8081 users:(("fossil")) LISTEN 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) LISTEN 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) LISTEN 0.0.0.0:22 users:(("sshd"),("systemd")) LISTEN [::]:443 users:(("nginx"),("nginx"),("nginx")) LISTEN [::]:80 users:(("nginx"),("nginx"),("nginx")) LISTEN [::]:22 users:(("sshd"),("systemd")) LISTEN [::1]:8081 users:(("fossil")) This is fossil version 2.28 [52445a27f1] 2026-03-11 11:31:46 UTCGet your URL and password
Read the address that Fossil is served as (your instance's public DNS name by default) and the admin user:
sudo grep -E '^FOSSIL_(URL|ADMIN_USER)=' /root/fossil-info.txtFOSSIL_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ FOSSIL_ADMIN_USER=adminThe admin password is generated on this instance and has 24 letters and digits. It is on the FOSSIL_ADMIN_PASSWORD line of the same file:
sudo grep '^FOSSIL_ADMIN_PASSWORD=' /root/fossil-info.txtSign in from your browser
Open FOSSIL_URL in your browser. The certificate is self-signed and generated on your instance, so accept the browser warning once, or install your own certificate (Step 8). Because the repository is private, Fossil shows its login page straight away. Enter admin and the password from Step 4.
Use the address in FOSSIL_URL exactly. Fossil checks that every form you submit comes from that address, so the admin pages refuse changes made through a different name for the same instance. To serve Fossil under another name, set it with fossil-set-hostname (Step 8).
Browse the timeline, wiki and tickets
The Timeline is the home of the repository. It lists every check-in, wiki edit, ticket change and forum post in date order, with branch tags and links to each change. A new repository starts with Fossil's initial empty check-in. The example below also shows two check-ins pushed from a clone.
The Wiki tab holds pages written in Fossil wiki markup or Markdown, versioned with the code. Tickets is a full bug tracker, and Forum and Chat keep discussion next to the code.
Clone, commit and push from your workstation
Every clone is a full copy of the repository, its wiki and its tickets. On your workstation, clone over HTTPS as admin (Fossil asks for the password):
fossil clone https://admin@<your-fossil-host>/ myproject.fossilBecause the certificate is self-signed, Fossil shows its fingerprint and asks whether to accept it. Compare the fingerprint with the fingerprint of the certificate on the instance, then answer y or paste the fingerprint. To read the fingerprint on the instance, run:
sudo openssl x509 -noout -fingerprint -sha256 -in /etc/nginx/ssl/fossil.crt | cut -d= -f2 | tr -d ':' | tr 'A-F' 'a-f'Then open a working copy, commit and push:
mkdir myproject && cd myproject fossil open ../myproject.fossil echo "# My project" > README.md fossil add README.md fossil commit -m "Add README.md"With autosync, which is Fossil's default, fossil commit pulls before it commits and pushes afterwards. fossil push and fossil pull sync by hand.
Your own DNS name and certificate
Point a DNS record at the instance, then set the name that Fossil is served as. The command mints a new self-signed certificate for the name, updates nginx, Fossil's base URL and FOSSIL_URL in /root/fossil-info.txt, and keeps the name across reboots:
sudo fossil-set-hostname <your-domain>To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx. Running fossil-set-hostname again replaces it with a self-signed one:
sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/fossil.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/fossil.key
sudo nginx -t && sudo systemctl reload nginxUsers, passwords and public access
Add your team under Admin, Users, Add in the web interface. Give each person their own login and capabilities. The developer capability set, ei, lets someone check in and edit the wiki. The user list of a new repository shows that the nobody category (everyone who has not signed in) and the anonymous login have no capabilities.
You can check the same from the command line. fossil-repo runs the fossil command as the Fossil service user, so the Fossil service user stays the owner of the repository:
for u in admin nobody anonymous; do echo "$u: '$(sudo fossil-repo user capabilities $u -R /var/lib/fossil/repo.fossil)'"; done
sudo fossil-repo settings localauth -R /var/lib/fossil/repo.fossil
sudo fossil-repo settings self-register -R /var/lib/fossil/repo.fossil
sudo fossil-repo settings access-log -R /var/lib/fossil/repo.fossiladmin: 's' nobody: '' anonymous: '' localauth (local) 1 self-register (local) 0 access-log (local) 1In this output, localauth means that requests from the instance itself still need a login. Self-registration is off, and the access log records every login attempt with the client's real address.
To change the admin password, run fossil-set-password. Without arguments, it generates a new random password and records it in /root/fossil-info.txt. With --stdin, it reads your own password, which must have 16 to 64 letters and digits:
sudo fossil-set-password printf '%s\n' '<your-new-password>' | sudo fossil-set-password --stdinTo publish a project read-only, so that visitors can browse, clone and download but cannot make changes, give the nobody category the read capabilities. Do this only for code that you mean to make public:
sudo fossil-repo user capabilities nobody ghjorz -R /var/lib/fossil/repo.fossilSecurity checks
Port 80 sends browsers to HTTPS. The health endpoint answers without a login, for load balancer checks, but the repository itself does not. Confirm all three behaviours:
H=$(sudo grep '^FOSSIL_URL=' /root/fossil-info.txt | sed -E 's#^FOSSIL_URL=https://([^/]+)/.*#\1#')
curl -s -o /dev/null -w 'port 80: %{http_code} -> %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -w '\n' --resolve "$H:443:127.0.0.1" "https://$H/healthz"
curl -sk -L -o /dev/null -w 'timeline without a login: %{http_code} %{url_effective}\n' --resolve "$H:443:127.0.0.1" "https://$H/timeline"port 80: 301 -> https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ ok timeline without a login: 200 https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/login?g=timelineWith the admin password, the admin setup page opens. The commands read the password from the credentials file and send it to curl on stdin, so it never appears on a command line:
H=$(sudo grep '^FOSSIL_URL=' /root/fossil-info.txt | sed -E 's#^FOSSIL_URL=https://([^/]+)/.*#\1#');
J=$(mktemp)
sudo sed -n 's/^FOSSIL_ADMIN_PASSWORD=\(.*\)$/u=admin\&p=\1/p' /root/fossil-info.txt \
| curl -sk --resolve "$H:443:127.0.0.1" -c "$J" --data @- -o /dev/null "https://$H/login" curl -sk --resolve "$H:443:127.0.0.1" -b "$J" -o /dev/null -w 'admin /setup: %{http_code}\n' "https://$H/setup" rm -f "$J"admin /setup: 200Fossil takes its base URL from configuration (/etc/fossil/fossil.env, written by fossil-set-hostname), never from the request. Confirm it:
cat /etc/fossil/fossil.env ps -o args= -C fossil | head -1FOSSIL_BASEURL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com /usr/local/bin/fossil server --localhost --port 8081 --https --baseurl https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com --max-latency 300 /var/lib/fossil/repo.fossilThe instance generated the certificate, and it names the instance's own addresses. Confirm it:
H=$(sudo grep '^FOSSIL_URL=' /root/fossil-info.txt | sed -E 's#^FOSSIL_URL=https://([^/]+)/.*#\1#')
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltNamesubject=CN = ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhostFossil runs unprivileged in a sandbox, and neither Fossil nor nginx can reach the instance metadata service. Confirm both:
systemctl show fossil -p User -p ProtectSystem -p NoNewPrivileges -p IPAddressDeny
systemctl show nginx -p IPAddressDenyIPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32 User=fossil ProtectSystem=strict NoNewPrivileges=yes IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32nginx passes Fossil the configured host name and the real client address from the connection, never values that a client sends. It closes the connection for any host name that the instance does not serve. Confirm the proxy settings:
grep -E 'proxy_set_header (Host|X-Forwarded-For|X-Real-IP)' /etc/nginx/sites-available/sac-fossil curl -s -o /dev/null -w 'unknown host name: %{http_code}\n' -H 'Host: example.com' http://127.0.0.1/ || trueproxy_set_header Host $fossil_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; unknown host name: 000The 000 result means that nginx closed the connection without an answer. In addition to Fossil's own cross-site request checks, nginx refuses a request that would change data when its Origin is another web site (HTTP 403).
Backups and upgrades
The whole repository, with its wiki, tickets and forum, is one file:
sudo ls -l /var/lib/fossil/total 216 -rw-r----- 1 fossil fossil 221184 Oct 19 14:06 repo.fossilBack it up with an EBS snapshot of the root volume. Alternatively, copy a consistent backup while Fossil keeps running, then send the copy to Amazon S3:
sudo fossil-repo backup -R /var/lib/fossil/repo.fossil /var/lib/fossil/backup-$(date +%F).fossilEvery clone is also a complete copy, so a workstation clone that you sync regularly is an off-instance backup too.
The image ships the Fossil version named in the listing as one static program at /usr/local/bin/fossil. To move to a newer release later, snapshot the instance, replace that file with the new official Linux build, and run sudo systemctl restart fossil.
Server components
Key paths on the image (component, then path):
Fossil /usr/local/bin/fossil (licence at /usr/share/doc/fossil/COPYRIGHT-BSD2.txt)
Repository (one SQLite file) /var/lib/fossil/repo.fossil
Base URL /etc/fossil/fossil.env
Service unit /etc/systemd/system/fossil.service
nginx site /etc/nginx/sites-available/sac-fossil
Certificate and key /etc/nginx/ssl/
First boot script /usr/local/sbin/fossil-firstboot.sh
Helpers /usr/local/sbin/fossil-set-hostname, /usr/local/sbin/fossil-set-password, /usr/local/sbin/fossil-repo
Credentials /root/fossil-info.txt (mode 0600)
Security recommendations
- Allow ports 22, 80 and 443 in the security group from your own networks only.
- Give each person their own Fossil login, with only the capabilities they need, rather than sharing
admin. - Keep the admin password secret, and rotate it if it may have been shared (Step 9).
- Use your own DNS name and a certificate that your browsers and Fossil clients trust (Step 8).
- Open anonymous read access only for code that you mean to publish (Step 9).
- Keep the operating system patched. Unattended security upgrades are on, and
sudo apt-get update && sudo apt-get upgradeapplies everything else. - Back up the repository regularly (Step 11).
Support and licensing
Fossil is open source under the BSD 2-Clause licence. A copy is at /usr/share/doc/fossil/COPYRIGHT-BSD2.txt. There is no per-user or per-CPU licence fee. The AMI price covers SecureAnyCloud's hardening, first boot setup and support.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24 hour response SLA
Deploy on AWS
Launch Fossil 2.28 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