APT, Yum, Raw, RubyGems, Composer, and Operating-System or Generic Artifact Formats: Guided Hands-On Workflow and Core Operations
Inspect five repository formats safely, build a disposable RubyGem native package plus a Raw immutable-file path, use Nexus REST/UI without exposing credentials, verify generated versus package-owned metadata, and keep client state isolated.
Learning objectives
- Create a disposable RubyGems hosted/proxy/group topology and a Raw hosted repository.
- Build a harmless synthetic gem and generic immutable files without using real organization namespaces.
- Upload through the documented Components API using a temporary permission-restricted credential file.
- Verify native RubyGems component identity separately from Raw path identity.
- Inspect APT, Yum, and Composer behavior safely through current repository recipes/fixtures without forcing unsupported clients onto the workstation.
Lab scope. Use a disposable self-hosted Community
instance at 127.0.0.1:8081. The verified feature floor
is Nexus 3.95.0; the required live repositories are RubyGems and
Raw, both available in CE. RubyGems current docs recommend client
3.4.12+. Commands are POSIX/Bash; PowerShell learners should use a
temporary credential file and Get-FileHash equivalents.
Do not use production repositories, keys, or namespaces.
1. Preflight: inspect first and isolate every local file
export NX_URL="http://127.0.0.1:8081"
export LAB="${TMPDIR:-/tmp}/nexus-ch11"
rm -rf "$LAB"
mkdir -p "$LAB/evidence" "$LAB/gem-src/lib" "$LAB/raw-src" "$LAB/downloads" "$LAB/gem-home"
chmod 700 "$LAB"
export GEM_HOME="$LAB/gem-home"
export GEM_PATH="$GEM_HOME"
curl -fsS "$NX_URL/service/rest/v1/status" | tee "$LAB/evidence/status.txt"
curl -fsS "$NX_URL/service/rest/v1/repositories" | tee "$LAB/evidence/repositories-before.json"
ruby --version 2>&1 | tee "$LAB/evidence/ruby-version.txt" || true
gem --version 2>&1 | tee "$LAB/evidence/rubygems-version.txt" || true
If Ruby/RubyGems is absent, the learner can still complete the Nexus format comparison and Raw path; the native RubyGems commands become a fixture walkthrough. Do not install system-wide packages merely to satisfy the lesson.
2. Create four disposable repositories
In Nexus Settings → Repository → Repositories, create:
| Name | Recipe | Purpose |
|---|---|---|
academy-ch11-gem-hosted |
rubygems (hosted) | Authoritative synthetic gem publication target; Disable redeploy if available. |
academy-ch11-gem-proxy |
rubygems (proxy) |
Remote https://rubygems.org/; harmless
public-resolution comparison.
|
academy-ch11-gem-group |
rubygems (group) | Members hosted first, proxy second; client read aggregation. |
academy-ch11-raw-hosted |
raw (hosted) | Path-addressed generic files; strict content-type validation left enabled unless a demonstrated MIME mismatch requires a documented exception. |
Create a disposable publisher identity with read/browse on the group and read/browse/add on both hosted repositories. Do not grant repository administration to the package publisher.
3. Build a temporary Basic-auth boundary without leaking a password
read -r -p 'Disposable Nexus username: ' NX_USER
read -r -s -p 'Disposable Nexus password: ' NX_PASS; echo
export NX_AUTH_FILE="$LAB/nexus.netrc"
umask 077
printf 'machine 127.0.0.1 login %s password %s
' "$NX_USER" "$NX_PASS" > "$NX_AUTH_FILE"
unset NX_PASS
curl --netrc-file keeps the password out of the command
arguments. The file is inside the disposable workspace with
restrictive permissions and is deleted during cleanup.
4. Build a synthetic RubyGem locally
cd "$LAB/gem-src"
cat > learner_ch11_native.gemspec <<'EOF'
Gem::Specification.new do |s|
s.name = "learner_ch11_native"
s.version = "1.0.0"
s.summary = "Disposable Nexus format lab"
s.authors = ["DevOps Academy Lab"]
s.files = ["lib/learner_ch11_native.rb"]
s.require_paths = ["lib"]
s.license = "MIT"
end
EOF
cat > lib/learner_ch11_native.rb <<'EOF'
module LearnerCh11Native
IDENTITY = "learner_ch11_native/1.0.0"
end
EOF
gem build learner_ch11_native.gemspec | tee "$LAB/evidence/gem-build.txt"
python - <<'PYI' | tee "$LAB/evidence/gem-sha256.txt"
from pathlib import Path
import hashlib, os
p = Path(os.environ['LAB'], 'gem-src', 'learner_ch11_native-1.0.0.gem')
print(hashlib.sha256(p.read_bytes()).hexdigest(), p.name)
PYI
The .gem contains the package metadata the RubyGems
ecosystem expects. Nexus will identify the component by gem name and
version, not by an arbitrary Raw path.
5. Upload through the format-aware Components API
curl --fail --silent --show-error --netrc-file "$NX_AUTH_FILE" -X POST "$NX_URL/service/rest/v1/components?repository=academy-ch11-gem-hosted" -F "rubygems.asset=@$LAB/gem-src/learner_ch11_native-1.0.0.gem" -o /dev/null -w 'HTTP %{http_code}
' | tee "$LAB/evidence/gem-upload.txt"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/components?repository=academy-ch11-gem-hosted" | tee "$LAB/evidence/gem-components.json"
A successful upload returns HTTP 204. The component list should show
learner_ch11_native version 1.0.0. Nexus
also updates the repository information required for RubyGems
clients.
6. Consume the native package without changing the user's normal Gem home
If anonymous read is enabled on the disposable instance, the cleanest native protocol demonstration is:
export GEM_GROUP="$NX_URL/repository/academy-ch11-gem-group/"
gem install learner_ch11_native --version 1.0.0 --source "$GEM_GROUP" --clear-sources --no-document 2>&1 | tee "$LAB/evidence/gem-install.txt"
ruby -e 'require "learner_ch11_native"; puts LearnerCh11Native::IDENTITY' | tee "$LAB/evidence/gem-runtime.txt"
If anonymous read is disabled, do not put credentials into the source URL or command history. Use the authenticated download path below, then let the native Gem client validate/install the local package:
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/repository/academy-ch11-gem-group/gems/learner_ch11_native-1.0.0.gem" -o "$LAB/downloads/learner_ch11_native-1.0.0.gem"
{ gem specification "$LAB/downloads/learner_ch11_native-1.0.0.gem" name; gem specification "$LAB/downloads/learner_ch11_native-1.0.0.gem" version; } | tee "$LAB/evidence/gem-specification.txt"
gem install --local "$LAB/downloads/learner_ch11_native-1.0.0.gem" --no-document | tee "$LAB/evidence/gem-local-install.txt"
This fallback proves the native package semantics without teaching
credential-in-URL behavior. It does not claim that
gem install --local exercised Nexus resolution; the
preceding authenticated download is the repository-boundary
evidence.
7. Create generic immutable files and publish them to Raw
cat > "$LAB/raw-src/learner-tool-1.0.0.txt" <<'EOF'
name=learner-tool
version=1.0.0
purpose=generic path-addressed training artifact
EOF
python - <<'PYI'
from pathlib import Path
import json, os
p=Path(os.environ['LAB'],'raw-src','learner-tool-1.0.0.json')
p.write_text(json.dumps({'name':'learner-tool','version':'1.0.0','kind':'generic'}, indent=2)+'
')
PYI
python - <<'PYI' | tee "$LAB/evidence/raw-sha256-before.txt"
from pathlib import Path
import hashlib, os
for p in sorted(Path(os.environ['LAB'],'raw-src').iterdir()):
print(hashlib.sha256(p.read_bytes()).hexdigest(), p.name)
PYI
curl --fail --silent --show-error --netrc-file "$NX_AUTH_FILE" -X POST "$NX_URL/service/rest/v1/components?repository=academy-ch11-raw-hosted" -F "raw.directory=releases/learner-tool/1.0.0" -F "raw.asset1=@$LAB/raw-src/learner-tool-1.0.0.txt" -F "raw.asset1.filename=learner-tool-1.0.0.txt" -F "raw.asset2=@$LAB/raw-src/learner-tool-1.0.0.json" -F "raw.asset2.filename=learner-tool-1.0.0.json" -o /dev/null -w 'HTTP %{http_code}
' | tee "$LAB/evidence/raw-upload.txt"
Raw keeps each file as a separate component. The shared directory is a naming convention created by the operator, not a native package identity understood by Nexus.
8. Retrieve by exact path and prove byte identity
for f in learner-tool-1.0.0.txt learner-tool-1.0.0.json; do
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/repository/academy-ch11-raw-hosted/releases/learner-tool/1.0.0/$f" -o "$LAB/downloads/$f"
done
python - <<'PYI' | tee "$LAB/evidence/raw-sha256-after.txt"
from pathlib import Path
import hashlib, os
base=Path(os.environ['LAB'])
for name in ['learner-tool-1.0.0.txt','learner-tool-1.0.0.json']:
a=hashlib.sha256((base/'raw-src'/name).read_bytes()).hexdigest()
b=hashlib.sha256((base/'downloads'/name).read_bytes()).hexdigest()
print(name, a, b, 'MATCH' if a==b else 'MISMATCH')
PYI
9. Inspect the other native formats without forcing a workstation conversion
| Format | Read-only inspection exercise | What to verify |
|---|---|---|
| APT |
Create or inspect an apt (proxy) recipe and
view Distribution/Flat/Signing fields.
|
There is no apt (group) recipe; hosted requires
signing configuration; metadata and package signatures are
distinct.
|
| Yum |
Create/inspect a disposable yum (hosted) or use
a JSON fixture of its settings.
|
Repodata Depth exists for hosted; group is supported; metadata signing is proxy/group-specific. |
| Composer | Inspect recipe list only unless exact build/edition is verified. | Proxy is v2-only; hosted/group require 3.95.0+; disable Packagist for Nexus-only client routing. |
| RubyGems | Already exercised live. | Native component name/version and group aggregation. |
| Raw | Already exercised live. | Path and bytes only; no dependency/index semantics. |
10. Challenge: select the repository before you create it
For each artifact, choose native or Raw and explain the client-visible evidence that proves your choice:
-
An Ubuntu agent needs dependency-resolved
.debpackages. - A firmware updater already knows an exact HTTPS path for a signed binary bundle.
- A Ruby service uses Bundler and needs internal plus public gems through one endpoint.
- A PHP service needs a private Composer package and must not reach Packagist directly.
The correct answer is not “native is always better.” The correct answer follows the consumer protocol and required metadata.
11. Cleanup and evidence
Keep the evidence directory until review. Then delete only
academy-ch11-gem-group,
academy-ch11-gem-proxy,
academy-ch11-gem-hosted, and
academy-ch11-raw-hosted through Nexus UI/API. Do not
remove blob files or database rows manually.
rm -f "$NX_AUTH_FILE"
unset NX_USER NX_AUTH_FILE GEM_HOME GEM_PATH
# After evidence review only:
# rm -rf "$LAB"
Knowledge check
Why did the gem upload use rubygems.asset rather than raw.asset1?
The target repository is RubyGems and the Components API is format-aware. Using the native upload field lets Nexus parse the gem as a RubyGems component and update native repository information.
What does the Raw directory releases/learner-tool/1.0.0 mean to Nexus?
It is an operator-chosen HTTP path. Raw does not infer that the files form one package or understand dependencies/versions from the directory structure.
Why is the authenticated RubyGems fallback split into curl plus gem install --local?
It keeps credentials in a temporary netrc instead of embedding them in the gem source URL, while still letting the native Gem client validate/install the downloaded .gem.
What should be deleted during cleanup?
Only the named disposable Nexus repositories through supported Nexus operations and the local lab directory after evidence review; never blob/database internals.
Why is Composer not required in this live lab?
Current primary Sonatype pages conflict on Composer edition wording, while hosted/group support is also version-gated at 3.95.0. The mandatory path stays on unambiguous Community features.
12. Summary and next step
The live workflow demonstrated the distinction the chapter is built around: RubyGems has native name/version/index semantics, while Raw exposes operator-defined paths and unchanged bytes. APT, Yum, and Composer require their own metadata and trust rules rather than being variations of the same upload pattern.
Lesson 3 uses those observations to make architecture choices deliberately.
Official references and version notes
- Sonatype: Formats — current proxy/hosted/group support matrix.
- Sonatype: APT Repositories.
- Sonatype: Yum Repositories and GPG signatures for Yum.
- Sonatype: Raw Repositories.
- Sonatype: RubyGems Repositories.
- Sonatype: Composer Repositories.
- Sonatype: Components API — APT, Raw, and RubyGems upload field names.
- Sonatype: Community/Pro feature matrix and Community Edition onboarding.
- Sonatype: 2026 self-hosted release notes.
- RubyGems command reference, Composer documentation, and distribution-specific APT/DNF/RPM documentation for client trust configuration.
Version-sensitive statements were rechecked on 2026-08-26. The chapter uses Nexus Repository 3.95.0 as the verified feature floor because current Sonatype release notes explicitly list it as released on 2026-08-05 and document Composer hosted/group support there. Some adjacent Sonatype download/version-status pages still lag at 3.94.1, so record the exact version/edition on your lab instance before applying version-specific steps. If you are continuing with a later 3.95.x instance from a prior chapter, it satisfies this chapter's 3.95.0 feature floor. Mandatory labs use Community-compatible RubyGems and Raw features and do not depend on the documentation-sensitive Composer entitlement boundary.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.