Gemstash on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of Gemstash 2.8 on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Gemstash is a RubyGems server built by the RubyGems and Bundler team. It does two jobs on a single node: it is a pull through cache and mirror of rubygems.org, so bundle and gem install fetch public gems through one shared endpoint that caches every gem for the whole team, and it is a private gem repository for pushing and hosting your own gems.
The image installs the pinned Gemstash 2.8.2 gem under the Ubuntu 24.04 archive Ruby (3.2, security supported) and runs it under systemd. The Gemstash server binds to loopback only (127.0.0.1:9292); nginx is the sole network facing surface on port 80 and reverse proxies to it. Public gems are served from the mirror without authentication; pushing and yanking private gems is gated by API keys.
Security by design — no baked API key. Gemstash gates private gem push and yank with API keys created by gemstash authorize; fetching cached public gems needs no authentication. This image ships no API key at all. On the very first boot, before the port is reachable, a unique push API key is generated with openssl rand -hex 24, authorized, and written to /root/gemstash-credentials.txt (mode 0600, root only), and an unauthenticated private push is proven to be rejected with HTTP 401 before the instance is marked ready. The gem cache, private gems and SQLite database are created fresh per VM, so the captured image carries no cached gem, no private gem and no key.
What is included:
-
Gemstash 2.8.2 (MIT licensed), installed under the Ubuntu 24.04 archive Ruby (3.2), run under systemd as the unprivileged
gemstashsystem user -
A pull through mirror of rubygems.org and a private gem repository on one VM
-
The Gemstash server bound to loopback only (
127.0.0.1:9292) — never directly network exposed -
nginx on port 80 as the sole network facing surface, with
client_max_body_size 0for gem pushes -
A per instance push API key generated on first boot and stored in
/root/gemstash-credentials.txt(0600) — no baked key, no shared default -
A SQLite backed store with the gem cache and private gems under
/var/lib/gemstash, created fresh per VM -
Ubuntu 24.04 LTS base with latest security patches applied at build time
-
Azure Linux Agent for seamless cloud integration and SSH key injection
-
24/7 cloudimg support with guaranteed 24 hour response SLA
Prerequisites
-
Active Azure subscription, SSH public key, VNet + subnet in target region
-
Subscription to the Gemstash listing on Azure Marketplace
-
A machine with Ruby and Bundler installed to push private gems and to point
bundle/gemat the mirror
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) for a team mirror and private repository. Servers with many concurrent installs or a large cache should use Standard_D2s_v5 (2 vCPU, 8 GB RAM) or larger, and a larger OS disk for the cache.
Step 1: Deploy from the Azure Portal
Search Gemstash in Marketplace, select the cloudimg publisher, click Create. NSG rules: TCP 22 (admin) and TCP 80 (the gem server) from your client networks.
Step 2: Deploy from the Azure CLI
RG="gemstash-prod"; LOCATION="eastus"; VM_NAME="gemstash-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/gemstash/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name gemstash-vnet --address-prefix 10.100.0.0/16 --subnet-name gemstash-subnet --subnet-prefix 10.100.1.0/24
az network nsg create -g "$RG" --name gemstash-nsg
az network nsg rule create -g "$RG" --nsg-name gemstash-nsg --name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name gemstash-nsg --name allow-http --priority 110 \
--destination-port-ranges 80 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username azureuser --ssh-key-values "$SSH_KEY" \
--vnet-name gemstash-vnet --subnet gemstash-subnet --nsg gemstash-nsg --public-ip-sku Standard
Step 3: Connect via SSH and retrieve your API key
The per instance push API key is generated on the first boot and stored in a root only file. Connect over SSH and read it:
ssh azureuser@<public-ip>
sudo cat /root/gemstash-credentials.txt
The file contains the mirror URL, the private repository URL and the per instance API key, plus quick start commands. Store the key somewhere safe — it is unique to this VM and it is what authorizes pushing private gems.
Step 4: Verify the gemstash and nginx services
Confirm both services are active and that Gemstash is bound to loopback only. In the listener output, 127.0.0.1:9292 (Gemstash) is loopback only, while nginx listens publicly on :80:
systemctl status gemstash nginx --no-pager
gemstash version
ss -tln | grep -E ':9292|:80 '

Step 5: Check the health endpoint
The /healthz endpoint is served by nginx without authentication (ideal for load balancer and Azure health probes) and returns HTTP 200:
curl -o /dev/null -w 'healthz: HTTP %{http_code}\n' http://127.0.0.1/healthz
Step 6: Use Gemstash as a pull through mirror for public gems
Gemstash caches public gems from rubygems.org and serves them from the mirror at the root URL. Fetching public gems needs no authentication. From the VM you can confirm the pull through works by fetching a public gem through Gemstash — it downloads from rubygems.org the first time and caches it for everyone after:
gem fetch --clear-sources --source http://127.0.0.1/ rake

On your developer machines or CI, point Bundler at the mirror so every bundle install goes through Gemstash. Replace <public-ip> with your VM's public IP or DNS name:
bundle config mirror.https://rubygems.org http://<public-ip>
From then on, bundle install fetches public gems through your Gemstash server, cached and shared across the team.
Step 7: Push and host private gems
Private gems are pushed to the /private path and require the per instance API key — an unauthenticated push, or a push with the wrong key, is rejected with HTTP 401. The screenshot below shows the full proof: a push with no key returns 401, a push with a wrong key returns 401, a push with the per instance key returns 200, and the private gem is then fetchable.

To push one of your own gems, add your per instance API key to Rubygems' credentials, then push to the /private host. Replace <public-ip> and <GEMSTASH_API_KEY> with your VM's IP and the key from Step 3:
mkdir -p ~/.gem && chmod 0700 ~/.gem
echo -e "---\n:cloudimg: <GEMSTASH_API_KEY>" >> ~/.gem/credentials
chmod 0600 ~/.gem/credentials
gem push --key cloudimg --host http://<public-ip>/private my-gem-0.1.0.gem
Install a private gem in a Gemfile by wrapping it in a source block that points at /private, and let Gemstash serve everything else from the mirror:
source "http://<public-ip>"
gem "rake"
source "http://<public-ip>/private" do
gem "my-gem"
end
Step 8: How Gemstash is wired and secured
The Gemstash configuration at /etc/gemstash/config.yml binds the server to loopback only, stores its data under /var/lib/gemstash with the SQLite adapter, and points the mirror upstream at rubygems.org. The per instance API key lives in the root only credentials file, and gemstash authorize --list shows exactly one key on a fresh VM:
grep -E ':bind|:base_path|:db_adapter|:rubygems_url|:protected_fetch' /etc/gemstash/config.yml
sudo stat -c '%n %a %U:%G' /root/gemstash-credentials.txt

Step 9: Use your own domain and TLS (production)
Gemstash is served over plain HTTP on port 80 for convenience. For production, front it with your own DNS name and a CA signed certificate:
-
Point a DNS record (for example
gems.example.com) at the VM public IP. -
Add a TLS server block to nginx (for example with a Let's Encrypt certificate via certbot, or your internal CA), proxying to
http://127.0.0.1:9292, then runsudo nginx -t && sudo systemctl reload nginx. -
Clients then use
https://gems.example.comfor both the mirror and/private.
Step 10: Protect private fetching and scale the database
-
Protected fetching. By default, private gems can be fetched without a key. To require an API key with the
fetchpermission for private gem downloads too, runsudo gemstash setupand enable protected fetching, then restart Gemstash. Clients pass the key as the HTTP Basic auth username on the/privatesource. -
PostgreSQL for durability. For larger or highly durable deployments, switch the store from SQLite to an on box PostgreSQL by setting
:db_adapter: postgresand the connection options in/etc/gemstash/config.yml, then restart Gemstash. SQLite is the self contained default and needs no database password.
Step 11: Server components
| Component | Version / Detail |
|---|---|
| Gemstash | 2.8.2 (MIT), by the RubyGems and Bundler team |
| Ruby | Ubuntu 24.04 archive Ruby 3.2 (security supported) |
| Server binding | 127.0.0.1:9292 (loopback only) |
| Front proxy | nginx (:80), client_max_body_size 0, /healthz 200 |
| Store | SQLite adapter, base path /var/lib/gemstash (gem cache + private gems + DB) |
| Credential | per instance push API key, generated first boot, /root/gemstash-credentials.txt (0600) |
| Operating system | Ubuntu 24.04 LTS (patched at build) |
| License | MIT (Gemstash) |
Step 12: Managing the Gemstash service
sudo systemctl status gemstash
sudo systemctl restart gemstash
sudo journalctl -u gemstash -f
sudo systemctl reload nginx
The gem cache, private gems and SQLite database live under /var/lib/gemstash and persist across service restarts and VM reboots. To create more API keys or scope permissions, run sudo gemstash authorize push yank --config-file /etc/gemstash/config.yml; to rotate or remove a key, use sudo gemstash authorize --remove --key <key> --config-file /etc/gemstash/config.yml.
Step 13: Security recommendations
-
Restrict the NSG. Allow TCP 80 (and 22 for admin) only from the client networks that need the mirror and the private repository. Do not expose Gemstash to the public internet unless you intend to.
-
Front with TLS (Step 9) for production so gem traffic and API keys are encrypted in transit.
-
Enable protected fetching (Step 10) if your private gems must not be downloadable without a key.
-
Rotate the API key. Remove the per instance key and authorize a new one with
gemstash authorizeif it is ever exposed. -
Keep the OS patched. Unattended security upgrades remain enabled on the running VM.
-
Back up
/var/lib/gemstashto preserve your private gems and cache.
Step 14: Support and Licensing
Gemstash is free and open source software built by the RubyGems and Bundler team, distributed under the MIT License. This cloudimg image bundles the unmodified upstream gem. cloudimg provides the packaging, hardening, per instance API key automation and 24/7 support with a guaranteed 24 hour response SLA. This image is not affiliated with, endorsed by, or sponsored by the RubyGems project; the Gemstash name is used only to identify the software shipped in this image.
Deploy on Azure
Find Gemstash on Ubuntu 24.04 on the Azure Marketplace, published by cloudimg. Deploy from the Portal or the Azure CLI as shown above.
Need Help?
Email support@cloudimg.co.uk for deployment help, configuration questions, or licensing enquiries.