miniserve 0.35.0
Overview
miniserve is a small, fast, self-hosted HTTP file server. It serves one directory through a clean web interface: browse folders, download files or whole folders as tar.gz or zip archives, upload files from the browser and create directories. It is a single static Rust binary with no database, so it starts instantly and stays light.
Upstream miniserve is public, with no login. This SecureAnyCloud image, miniserve 0.35.0 on Ubuntu 24.04 for Amazon EC2, turns that around. It is one version of the SecureAnyCloud SFTP and file server listing on AWS Marketplace, which you choose as the software version when you subscribe and launch.
What you get:
- HTTPS from the first boot. nginx serves the web interface on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
- A password unique to your instance. HTTP Basic Auth is required for the user SecureAnyCloud. The password is generated on first boot and written to /root/miniserve-info.txt. Only its sha256 hash is stored in the service configuration, and no password is baked into the image.
- Fail closed. miniserve refuses to start until first boot has written that credential, and nginx refuses to start until first boot has finished, so files are never served without a login.
- One directory only. miniserve serves
/srv/miniserve, never the whole filesystem, and listens on loopback127.0.0.1:8080behind nginx. - Hardened defaults. miniserve runs as its own unprivileged user in a systemd sandbox. Both miniserve and nginx are blocked from the instance metadata service, the AMI requires IMDSv2, and requests for any host name the instance does not serve are dropped.
- A fully patched base. Ubuntu 24.04 LTS is patched at build time, with unattended security upgrades switched on.
- Image contents: miniserve 0.35.0 (
/usr/local/bin/miniserve, the official release binary, 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 you use for management)
- A security group that allows TCP 22 (SSH), TCP 443 (the web interface) and TCP 80 (redirects to HTTPS) from your own networks only
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick miniserve 0.35.0 on Ubuntu 24.04 as the software version, choose your Region, then choose Launch through EC2.
Choose m5.large, your key pair and a security group as described above. miniserve itself needs very little, so size the instance for your users and file sizes. Set an EBS volume size that fits the files you plan to store, because the served directory lives on the root volume.
To launch with the AWS CLI instead, 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 \
--block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=30,VolumeType=gp3}' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=miniserve}]'Connect to your instance
Connect over SSH as the user for your version:
Version SSH user
miniserve 0.35.0 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
On first boot, the instance generates the certificate and the password, then starts miniserve and nginx. Confirm that all three units are active and that the first-boot marker exists:
systemctl is-active miniserve nginx miniserve-firstboot
sudo test -f /var/lib/sac/data/miniserve-firstboot.done && echo FIRSTBOOT_DONEExpected output:
active active active FIRSTBOOT_DONECheck the listeners. Only SSH and nginx listen on public addresses, while miniserve listens on loopback:
sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sort0.0.0.0:22 users:(("sshd",...)) 0.0.0.0:443 users:(("nginx",...)) 0.0.0.0:80 users:(("nginx",...)) 127.0.0.1:8080 users:(("miniserve",...)) [::]:22 users:(("sshd",...)) [::]:443 users:(("nginx",...)) [::]:80 users:(("nginx",...))Get your URL and password
Read the credentials file as root:
sudo cat /root/miniserve-info.txtOnly root can read this file. It holds the URL, the user name and this instance's password:
MINISERVE_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ MINISERVE_USERNAME=sac MINISERVE_PASSWORD=<MINISERVE_PASSWORD>The URL uses the instance's public DNS name from EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too. To serve the files under your own DNS name, see Step 9.
Sign in from your browser
Open the MINISERVE_URL in your browser. The certificate is self-signed, so your browser asks you to confirm it the first time. To avoid the prompt, install your own certificate (Step 9). Sign in as SecureAnyCloud with the password from Step 4.
The listing shows every file and folder in /srv/miniserve with its size and date. It also provides buttons to download the whole folder as tar.gz or zip, a search box and a theme picker. The image ships with a README.txt and an examples/ folder. Replace them with your own files.
Upload files and create folders
Choose one or more files, or drag them anywhere into the window, then select Upload file. Uploads land in the folder you are viewing. To add a folder, type a name next to Create directory.
Open a folder to browse into it. The breadcrumb and the Parent directory row take you back up.
miniserve follows your browser's light or dark preference. Select Change theme to pick another color scheme.
Files you upload are owned by the miniserve user. An upload with the same name as an existing file is refused rather than overwriting it.
Use it from the command line
Without a login, the listing is refused. Port 80 redirects to HTTPS, and the health check stays open for load balancers. Verify this behavior:
H=$(sudo grep '^MINISERVE_URL=' /root/miniserve-info.txt | sed -E 's#^MINISERVE_URL=https://([^/]+)/.*#\1#')
curl -s -o /dev/null -w 'http:// -> %{http_code} %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -sk -o /dev/null -w 'no login -> HTTP %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/"
curl -sk -o /dev/null -w 'healthcheck -> HTTP %{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/__miniserve_internal/healthcheck"http:// -> 301 https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ no login -> HTTP 401 healthcheck -> HTTP 200With the password, upload a file and download it back. The script passes the password to curl on standard input, so it never appears in the process list or your shell history:
sudo bash -c ' F=/root/miniserve-info.txt P=$(grep "^MINISERVE_PASSWORD=" $F | cut -d= -f2-) H=$(grep "^MINISERVE_URL=" $F | sed -E "s#^MINISERVE_URL=https://([^/]+)/.*#\1#") R="--resolve $H:443:127.0.0.1" auth() { printf "user = \"sac:%s\"\n" "$P"; } echo "hello from sac" > /root/report.txt auth | curl -K - -sk $R -o /dev/null -w "listing: HTTP %{http_code}\n" "https://$H/" auth | curl -K - -sk $R -o /dev/null -w "upload: HTTP %{http_code}\n" -F "file=@/root/report.txt" "https://$H/upload?path=/" echo "download: $(auth | curl -K - -sk $R "https://$H/report.txt")" rm -f /root/report.txt /srv/miniserve/report.txt 'listing: HTTP 200 upload: HTTP 303 download: hello from sacFrom your own machine, use the URL and password from Step 4. The -k flag accepts the self-signed certificate:
curl -k -u sac:<MINISERVE_PASSWORD> https://<public-ip>/
curl -k -u sac:<MINISERVE_PASSWORD> -F 'file=@./photo.jpg' 'https://<public-ip>/upload?path=/'You can also copy files straight into /srv/miniserve over SSH. Make them readable by the miniserve user with sudo chown -R miniserve:miniserve /srv/miniserve.
Security checks
Confirm that only the sha256 hash of the password is in the service configuration:
sudo grep '^MINISERVE_AUTH=' /etc/miniserve/miniserve.env | sed -E 's/[0-9a-f]{64}/<sha256>/'MINISERVE_AUTH=sac:sha256:<sha256>Inspect the certificate. It was generated on this instance and names its own addresses:
H=$(sudo grep '^MINISERVE_URL=' /root/miniserve-info.txt | sed -E 's#^MINISERVE_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:localhostConfirm that miniserve runs unprivileged in a sandbox, and that neither miniserve nor nginx can reach the instance metadata service:
systemctl show miniserve -p User -p ProtectSystem -p NoNewPrivileges -p IPAddressDeny
systemctl show nginx -p IPAddressDenyIPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32 User=miniserve ProtectSystem=strict NoNewPrivileges=yes IPAddressDeny=169.254.169.254/32 fd00:ec2::254/128 168.63.129.16/32Confirm that nginx passes miniserve a fixed host name and the real client address from the connection, never values a client sends:
grep -E 'proxy_set_header (Host|X-Forwarded-For|X-Real-IP)' /etc/nginx/sites-available/sac-miniserveproxy_set_header Host 127.0.0.1:8080; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr;nginx also refuses an upload whose Origin is another website (HTTP 403), because browsers resend Basic Auth credentials on cross-site requests.
Use your own DNS name and certificate
Point a DNS record at the instance, then set the name that miniserve is served under. The command creates a new self-signed certificate for the name, updates nginx and the URL in /root/miniserve-info.txt, and keeps the name across reboots:
sudo miniserve-set-hostname <your-domain>To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx. Running miniserve-set-hostname again replaces it with a self-signed certificate.
sudo install -m 0644 <your-domain>.crt /etc/ssl/miniserve/miniserve.crt
sudo install -m 0600 <your-domain>.key /etc/ssl/miniserve/miniserve.key
sudo nginx -t && sudo systemctl reload nginxConfiguration and password rotation
miniserve reads its settings from /etc/miniserve/miniserve.env. The image sets these variables:
Variable Meaning Image default
MINISERVE_PATH Directory served /srv/miniserve
MINISERVE_INTERFACE / MINISERVE_PORT Bind address and port 127.0.0.1 / 8080 (nginx fronts it)
MINISERVE_AUTH Basic Auth credential sac:sha256:... (generated on first boot)
MINISERVE_ALLOWED_UPLOAD_DIR Where uploads are allowed / (anywhere in the served directory)
MINISERVE_MKDIR_ENABLED Allow creating directories true
MINISERVE_ENABLE_TAR_GZ / MINISERVE_ENABLE_ZIP Folder archive downloads true
MINISERVE_TITLE Page title SecureAnyCloud file server
Run miniserve --help to list every option and its environment variable. After you edit the file, run sudo systemctl restart miniserve.
To set a new password, store its sha256 hash and restart the service. Start the first line with a space so most shells keep it out of your history:
HASH=$(printf '%s' '<new-password>' | sha256sum | awk '{print $1}')
sudo sed -i -E "s/^MINISERVE_AUTH=.*/MINISERVE_AUTH=sac:sha256:${HASH}/" /etc/miniserve/miniserve.env
sudo systemctl restart miniserveThen update or delete MINISERVE_PASSWORD in /root/miniserve-info.txt so the file does not keep the old password.
Uploads are limited to 5 GB each by client_max_body_size in /etc/nginx/sites-available/sac-miniserve. To change the limit, edit it there and reload nginx.
Server components
Component Path
miniserve binary /usr/local/bin/miniserve
Served directory /srv/miniserve
Configuration /etc/miniserve/miniserve.env (root only)
Service unit /etc/systemd/system/miniserve.service
nginx site /etc/nginx/sites-available/sac-miniserve
Certificate and key /etc/ssl/miniserve/
First boot script /usr/local/sbin/miniserve-firstboot.sh
Host name helper /usr/local/sbin/miniserve-set-hostname
Credentials /root/miniserve-info.txt (mode 0600)
/usr/local/bin/miniserve --version sudo journalctl -u miniserve -u nginx --no-pager -n 5 -o catSecurity recommendations
- Allow ports 22, 80 and 443 in the security group from your own networks only.
- Keep the per-instance password secret, and rotate it if it may have been shared (Step 10).
- Use your own DNS name and a certificate your clients trust (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 your files by snapshotting the root volume or by copying
/srv/miniserveelsewhere.
Support and licensing
miniserve is open source under the MIT license, with no per-user or per-CPU license 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 miniserve 0.35.0 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