Gemstash
Overview
Gemstash 2.8 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. It is also a private gem repository for pushing and hosting your own gems.
This guide covers deploying and configuring Gemstash 2.8 on Ubuntu 24.04 on Azure from SecureAnyCloud Azure Marketplace images. 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 only network-facing surface, on port 80, and reverse proxies to Gemstash. Public gems are served from the mirror without authentication. Pushing and yanking private gems requires an API key.
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). The instance is marked ready only after an unauthenticated private push is proven to be rejected with HTTP 401. 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 you get:
- Gemstash 2.8.2 (MIT licensed), installed under the Ubuntu 24.04 archive Ruby (3.2) and 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 only 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), with no baked key and 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 the latest security patches applied at build time
- Azure Linux Agent for cloud integration and SSH key injection
- 24/7 SecureAnyCloud support with a guaranteed 24-hour response SLA
Prerequisites
- An active Azure subscription, an SSH public key, and a VNet and subnet in the target region.
- A 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. For servers with many concurrent installs or a large cache, use Standard_D2s_v5 (2 vCPU, 8 GB RAM) or larger, and a larger OS disk for the cache.
Deploy from the Azure portal
Search for Gemstash in the Azure Marketplace, select the SecureAnyCloud publisher and choose Create.
Add Network Security Group rules for TCP 22 (admin) and TCP 80 (the gem server) from your client networks.
Deploy from the Azure CLI
RG="gemstash-prod"; LOCATION="eastus"; VM_NAME="gemstash-01" IMAGE_URN="sac1647283583153:gemstash:default:latest" 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
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:gemstash:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
--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 StandardConnect via SSH and retrieve your API key
The per-instance push API key is generated on first boot and stored in a root-only file. Connect over SSH and read it:
ssh azureuser@<public-ip>
sudo cat /root/gemstash-credentials.txtThe 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 authorizes pushing private gems.
Verify the gemstash and nginx services
Confirm that 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 'Check the health endpoint
The /healthz endpoint is served by nginx without authentication, so you can use it for load balancer and Azure health probes. It returns HTTP 200:
curl -o /dev/null -w 'healthz: HTTP %{http_code}\n' http://127.0.0.1/healthzUse 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, confirm that 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/ rakeOn your developer machines or CI, point Bundler at the mirror so that 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.
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.
Expect these results: 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:sac: <GEMSTASH_API_KEY>" >> ~/.gem/credentials chmod 0600 ~/.gem/credentials gem push --key sac --host http://<public-ip>/private my-gem-0.1.0.gemTo install a private gem from a Gemfile, wrap it in a source block that points at /private. Gemstash serves everything else from the mirror:
source "http://<public-ip>" gem "rake" source "http://<public-ip>/private" do gem "my-gem" endHow 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.txtUse 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. - Have clients use
https://gems.example.comfor both the mirror and/private.
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 fetch permission for private gem downloads too, run sudo gemstash setup, enable protected fetching, then restart Gemstash. Clients pass the key as the HTTP Basic auth username on the /private source.
PostgreSQL for durability. For larger or highly durable deployments, switch the store from SQLite to an on-box PostgreSQL by setting :db_adapter: postgres and the connection options in /etc/gemstash/config.yml, then restart Gemstash. SQLite is the self-contained default and needs no database password.
Server components
- 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 returns 200
- Store: SQLite adapter, base path /var/lib/gemstash (gem cache, private gems and database)
- Credential: per-instance push API key, generated on first boot, in /root/gemstash-credentials.txt (0600)
- Operating system: Ubuntu 24.04 LTS (patched at build time)
- License: MIT (Gemstash)
Manage the Gemstash service
sudo systemctl status gemstash
sudo systemctl restart gemstash
sudo journalctl -u gemstash -f sudo systemctl reload nginxThe 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.
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. If the per-instance key is ever exposed, remove it and authorize a new one with
gemstash authorize. - Keep the OS patched. Unattended security upgrades remain enabled on the running VM.
- Back up
/var/lib/gemstashto preserve your private gems and cache.
Support and licensing
Gemstash is free, open-source software built by the RubyGems and Bundler team and distributed under the MIT License. This SecureAnyCloud image bundles the unmodified upstream gem. SecureAnyCloud 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 SecureAnyCloud. Deploy from the Azure portal or the Azure CLI as shown above.
Need Help?
Email sales@secureanycloud.com for deployment help, configuration questions or licensing inquiries.
Deploy on Azure
Launch Gemstash 2.8 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com