Basic Configuration Examples
This guide provides configuration examples for common package managers and build tools to get you started with CloudRepo quickly.
Maven Configuration
Settings File (~/.m2/settings.xml)
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<servers>
<server>
<id>cloudrepo</id>
<username>your-username</username>
<password>$YOUR_API_TOKEN</password>
</server>
</servers>
<profiles>
<profile>
<id>cloudrepo</id>
<repositories>
<repository>
<id>cloudrepo</id>
<url>https://[org-id].mycloudrepo.io/repositories/maven-releases</url>
</repository>
</repositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>cloudrepo</activeProfile>
</activeProfiles>
</settings>
Project POM Configuration
<distributionManagement>
<repository>
<id>cloudrepo</id>
<url>https://[org-id].mycloudrepo.io/repositories/maven-releases</url>
</repository>
<snapshotRepository>
<id>cloudrepo</id>
<url>https://[org-id].mycloudrepo.io/repositories/maven-snapshots</url>
</snapshotRepository>
</distributionManagement>
Gradle Configuration
Build Script (build.gradle)
repositories {
maven {
url "https://[org-id].mycloudrepo.io/repositories/maven-releases"
credentials {
username = project.findProperty("cloudrepoUsername") ?: System.getenv("CLOUDREPO_USERNAME")
password = project.findProperty("cloudrepoPassword") ?: System.getenv("CLOUDREPO_API_TOKEN")
}
}
}
publishing {
publications {
maven(MavenPublication) {
from components.java
}
}
repositories {
maven {
url = "https://[org-id].mycloudrepo.io/repositories/maven-releases"
credentials {
username = cloudrepoUsername
password = cloudrepoPassword
}
}
}
}
Kotlin DSL (build.gradle.kts)
repositories {
maven {
url = uri("https://[org-id].mycloudrepo.io/repositories/maven-releases")
credentials {
username = findProperty("cloudrepoUsername")?.toString() ?: System.getenv("CLOUDREPO_USERNAME")
password = findProperty("cloudrepoPassword")?.toString() ?: System.getenv("CLOUDREPO_API_TOKEN")
}
}
}
Python Configuration
Pip Configuration (~/.pip/pip.conf)
[global]
index-url = https://member-email%40your-org.com:YOUR_API_TOKEN@[org-id].mycloudrepo.io/repositories/pypi/simple
Your CloudRepo username is an email address. Percent-encode the @ inside it as
%40: it is the form RFC 3986 specifies, and not every tool that reads this URL is as
forgiving as pip about a second literal @.
Warning
Do not set trusted-host for a CloudRepo host. pip documents --trusted-host
as marking a host trusted “even though it does not have valid or any HTTPS”. It
turns off certificate verification for that host, so the credential in the line
above would travel over a connection that accepts any certificate.
It buys you nothing here: CloudRepo serves *.mycloudrepo.io (the host in the
line above) with a publicly trusted certificate that validates with no extra
configuration. On a custom domain, trust the CA that issued the certificate you
configured.
Note
Behind a TLS-inspecting corporate proxy? If your network presents its own certificate authority, point pip at that CA rather than switching verification off:
# Try this FIRST, before setting anything: if the machine already trusts your
# corporate CA at the OS level, pip 24.2+ on Python 3.10+ reads the OS trust
# store on its own. Often no `cert` setting is needed at all.
# If you do need to name a file, use YOUR distribution's bundle -- the path is
# not portable, and pip fails hard ("Could not find a suitable TLS CA certificate
# bundle") when it does not exist:
# Debian / Ubuntu /etc/ssl/certs/ca-certificates.crt
# RHEL / Rocky / Alma /etc/pki/tls/certs/ca-bundle.crt
# macOS / Windows no equivalent -- build a bundle instead
pip config set global.cert /etc/ssl/certs/ca-certificates.crt
# Otherwise build a bundle yourself. Note the bootstrap: installing certifi needs
# a working TLS path to PyPI, which is the thing currently broken.
pip install certifi
CERTIFI_PEM="$(python3 -m certifi)" && \
cat "$CERTIFI_PEM" /path/to/corporate-ca.crt > /path/to/corporate-bundle.pem
pip config set global.cert /path/to/corporate-bundle.pem
The && is load-bearing: without it, a missing certifi would still let >
truncate the file, leaving you pinned to a bundle holding only the corporate CA.
Why a bundle rather than the bare corporate CA? Because on many machines cert
replaces the trust store outright, and a corporate-CA-only file then breaks every
install from pypi.org.
Whether it replaces depends on Python’s version, not pip’s: pip enables its
truststore backend only on Python 3.10+. So on Python 3.9 or older – which
includes the system Python on RHEL, Rocky and Alma 9 – cert still replaces,
however new your pip is. And pip 24.1 and earlier replace regardless of Python –
pip enabled truststore by default at 24.2.
A concatenated bundle is correct in every combination, which is why it is the form
given here. (pip’s --cert help still reads “If provided, overrides the default”.)
pip also honors PIP_CERT, REQUESTS_CA_BUNDLE and CURL_CA_BUNDLE for the
same purpose. All three beat pip.conf. Measured precedence, strongest first:
REQUESTS_CA_BUNDLE / CURL_CA_BUNDLE > PIP_CERT > pip.conf global.cert
So if an install still fails after you have set global.cert, check the
environment for all three before changing anything else, starting with
REQUESTS_CA_BUNDLE, because it wins over the other two.
Supplying the Token from Your Keyring
The index-url form above is the standard pip configuration and it works. It does
store your API token in plain text in pip.conf, and any command that echoes the
index URL will print it. On a shared or long-lived machine you may prefer to have pip
read the token from your system keyring instead:
pip install keyring
keyring set [org-id].mycloudrepo.io member-email@your-org.com
With the token in the keyring, leave it out of the URL:
[global]
index-url = https://member-email%40your-org.com@[org-id].mycloudrepo.io/repositories/pypi/simple
Your CloudRepo username is an email address, so percent-encode the @ inside it as
%40 when it appears in a URL. pip itself parses the raw form correctly (it splits
the userinfo on the last @) and unquotes %40 back to a plain @, so
either spelling matches the username you gave keyring set. Prefer %40 anyway:
it is the form RFC 3986 specifies, and the other tools that read this same URL are not
all as forgiving.
Select the keyring mechanism with --keyring-provider (auto, disabled,
import, subprocess; the default is auto). The subprocess provider
requires the username to be present in the URL, as shown above.
Two things that make keyring look broken when it is not. pip only consults the keyring
after an unauthenticated request comes back 401, so the first request on each run
is expected to fail. And the auto provider does not query the keyring at all under
--no-input / PIP_NO_INPUT=1, which is common in CI. Pass
--keyring-provider import there. Use import rather than subprocess: pip
looks for the keyring CLI on PATH with its own scripts directory removed, so a
keyring installed by the pip install keyring above is exactly where pip refuses
to look, and subprocess silently falls back to disabled and sends no credential. On a headless machine with no Secret Service backend,
keyring may have no secure store to use at all; check keyring --list-backends
before relying on it.
Twine Configuration (~/.pypirc)
[distutils]
index-servers =
cloudrepo
[cloudrepo]
repository = https://[org-id].mycloudrepo.io/repositories/pypi
username = member-email@your-org.com
password = YOUR_API_TOKEN
.pypirc is read literally: it performs no shell expansion, so paste the token
itself rather than a $VARIABLE reference. Unlike the index-url form, the
username here is not part of a URL, so the @ needs no encoding.
Twine can also take the token from your keyring, which keeps it out of .pypirc
altogether:
keyring set https://[org-id].mycloudrepo.io/repositories/pypi member-email@your-org.com
In CI, where writing a .pypirc is awkward, twine also accepts the
TWINE_USERNAME and TWINE_PASSWORD environment variables.
Poetry Configuration
[[tool.poetry.source]]
name = "cloudrepo"
url = "https://[org-id].mycloudrepo.io/repositories/pypi/simple"
priority = "primary"
[tool.poetry.repositories.cloudrepo]
url = "https://[org-id].mycloudrepo.io/repositories/pypi"
npm Configuration
.npmrc Configuration
registry=https://[org-id].mycloudrepo.io/repositories/[repo-name]/
//[org-id].mycloudrepo.io/repositories/[repo-name]/:_authToken=YOUR_NPM_TOKEN
Mint the token with the npm CLI against your repository registry. Tokens are issued by the registry, not by the admin portal:
npm token create --registry=https://[org-id].mycloudrepo.io/repositories/[repo-name]/
Authenticate as a dedicated repository user (created in the admin portal under Users → Create a Repository User) rather than as the organization owner, and grant that user only the repositories the workload needs.
For CI/CD with base64-encoded auth:
registry=https://[org-id].mycloudrepo.io/repositories/[repo-name]/
//[org-id].mycloudrepo.io/repositories/[repo-name]/:_auth=BASE64_ENCODED
Note
Modern npm (v10+) rejects email addresses as usernames, so the
interactive npm login command does not work for CloudRepo customers.
Use the .npmrc token pattern above instead. (We’re working on a web
login flow that will let npm login open the admin portal in a
browser. Coming soon.)
Environment Variables
Secure Credential Management
Instead of hardcoding credentials, use environment variables. CI/CD workloads should use API tokens, never passwords:
export CLOUDREPO_USERNAME="member-email@your-org.com"
export CLOUDREPO_API_TOKEN="generated-from-admin-portal"
export CLOUDREPO_ORG="your-org-id"
API tokens are repo-scoped, revocable, and bound to a member identity. That makes them safer than portal passwords for any automated workload. Org owners cannot authenticate at package surfaces with their portal password; they must mint a token bound to a member user.
CI/CD Basic Setup
GitHub Actions
- name: Setup CloudRepo
env:
CLOUDREPO_USERNAME: ${{ secrets.CLOUDREPO_USERNAME }}
CLOUDREPO_API_TOKEN: ${{ secrets.CLOUDREPO_API_TOKEN }}
run: |
echo "<settings>...</settings>" > ~/.m2/settings.xml
Jenkins
pipeline {
environment {
CLOUDREPO_CREDS = credentials('cloudrepo-credentials')
}
stages {
stage('Deploy') {
steps {
sh 'mvn deploy -DcloudrepoUsername=$CLOUDREPO_CREDS_USR -DcloudrepoPassword=$CLOUDREPO_CREDS_PSW'
}
}
}
}
Testing Your Configuration
Verify Connection
Maven:
mvn dependency:get -DremoteRepositories=https://[org-id].mycloudrepo.io/repositories/maven-releases \
-DgroupId=com.example -DartifactId=test -Dversion=1.0
Python:
pip install --index-url https://[org-id].mycloudrepo.io/repositories/pypi/simple --dry-run your-package
npm:
# Verify authentication
npm whoami --registry=https://[org-id].mycloudrepo.io/repositories/[repo-name]/
# Verify registry connectivity
npm ping --registry=https://[org-id].mycloudrepo.io/repositories/[repo-name]/
Common Configuration Issues
Authentication Errors
401 Unauthorized: Check username/password
403 Forbidden: Verify repository permissions
Certificate Errors: Ensure proper HTTPS configuration
URL Format Issues
Maven: Ensure URL ends with repository name, not /
Python: Include /simple for pip index URLs
Proxy Configuration
If behind corporate proxy:
export HTTP_PROXY=http://proxy.company.com:8080
export HTTPS_PROXY=http://proxy.company.com:8080
export NO_PROXY=[org-id].mycloudrepo.io
Next Steps
Maven Repositories - Detailed Maven setup
Python Repositories - Advanced Python configuration
Continuous Integration and Deployment - Complete CI/CD integration
Repository Management - Repository administration