Skip to content

Import Packages from External Sources

Use Repod's import feature to mirror packages from public or internal repositories into your private repository without uploading files manually.


How importing works

Importing is a two-step process:

  1. Sync the index β€” Repod fetches the remote repository's package metadata (Packages.gz for APT, repomd.xml for RPM) and stores it in a searchable local catalog. No packages are downloaded at this stage.

  2. Import a package β€” Repod downloads the selected package binary from the remote source, runs it through the full security pipeline (antivirus, CVE scan, GPG verification), and publishes it to your distribution.


Step 1 β€” Add an external source

Navigate to Import β†’ Sources β†’ Add source, or use the API:

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  http://localhost:8000/import/sources \
  -d '{
    "name": "ubuntu-jammy",
    "type": "apt",
    "url": "http://archive.ubuntu.com/ubuntu",
    "distribution": "jammy",
    "components": ["main", "universe", "restricted"]
  }'
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  http://localhost:8000/import/sources \
  -d '{
    "name": "epel9",
    "type": "rpm",
    "url": "https://dl.fedoraproject.org/pub/epel/9/Everything/x86_64/"
  }'
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  http://localhost:8000/import/sources \
  -d '{
    "name": "almalinux9-appstream",
    "type": "rpm",
    "url": "https://repo.almalinux.org/almalinux/9/AppStream/x86_64/os/"
  }'

Common external sources

Name URL Distribution
Ubuntu 22.04 http://archive.ubuntu.com/ubuntu jammy
Ubuntu 24.04 http://archive.ubuntu.com/ubuntu noble
Debian 12 http://deb.debian.org/debian bookworm
Nginx stable http://nginx.org/packages/ubuntu jammy
Name URL
EPEL 9 https://dl.fedoraproject.org/pub/epel/9/Everything/x86_64/
EPEL 8 https://dl.fedoraproject.org/pub/epel/8/Everything/x86_64/
AlmaLinux 9 BaseOS https://repo.almalinux.org/almalinux/9/BaseOS/x86_64/os/
AlmaLinux 9 AppStream https://repo.almalinux.org/almalinux/9/AppStream/x86_64/os/
Nginx stable (RPM) https://nginx.org/packages/rhel/9/x86_64/

Step 2 β€” Sync the index

Syncing fetches the remote metadata and populates the local searchable catalog. This does not download any packages.

In the web UI: Import β†’ Sources β†’ [source name] β†’ Sync now

Via API (returns a real-time SSE stream):

curl -H "Authorization: Bearer $TOKEN" \
  http://localhost:8000/import/sources/1/sync

The response is a Server-Sent Events stream. Each line is a progress update:

data: {"status":"syncing","count":0,"message":"Fetching index..."}
data: {"status":"syncing","count":4521,"message":"Processing packages..."}
data: {"status":"done","count":4521,"message":"Index updated"}

Sync duration

Syncing a large source (e.g. Ubuntu jammy main+universe) can take 30–120 seconds due to the size of Packages.gz. Subsequent syncs are faster because only changed packages are processed.


Step 3 β€” Browse and select packages

After syncing, browse the package catalog under Import β†’ Catalog. Use the search box to find packages by name.

The catalog shows for each package:

  • Name, version, architecture
  • Size
  • Last sync timestamp
  • Description

Step 4 β€” Import a package

Select a package in the catalog and click Import, or use the API:

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  http://localhost:8000/import/packages \
  -d '{
    "source_id": 1,
    "package":   "nginx",
    "version":   "1.24.0-1ubuntu2",
    "arch":      "amd64",
    "distribution": "jammy"
  }'
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  http://localhost:8000/import/packages \
  -d '{
    "source_id": 2,
    "package":   "nginx",
    "version":   "1.24.0-1.el9.ngx",
    "arch":      "x86_64",
    "distribution": "almalinux9"
  }'

The import runs the full security pipeline and returns a result:

{
  "status": "published",
  "package": "nginx",
  "version": "1.24.0",
  "distribution": "jammy",
  "pipeline": {
    "antivirus": "clean",
    "cve_scan": "no_findings",
    "gpg": "unsigned_pass",
    "dependencies": "satisfied"
  }
}

Batch import

To import multiple packages at once, use a shell loop:

PACKAGES=(nginx curl libssl3 openssl)
SOURCE_ID=1
DIST=jammy

for pkg in "${PACKAGES[@]}"; do
  echo "Importing $pkg..."
  curl -sf -X POST -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    http://localhost:8000/import/packages \
    -d "{
      \"source_id\": $SOURCE_ID,
      \"package\": \"$pkg\",
      \"distribution\": \"$DIST\"
    }"
  echo
done

Omit version to import the latest

If version is omitted, Repod imports the latest available version from the synced index.


Air-gapped environments

In air-gapped networks where Repod cannot reach external URLs directly, you can pre-download packages and upload them via the standard upload endpoint:

# On an internet-connected machine
apt-get download nginx
scp nginx_1.24.0-1_amd64.deb repod-server:/tmp/

# On the Repod server β€” upload the .deb
TOKEN=$(curl -s ... | jq -r .access_token)
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -F "file=@/tmp/nginx_1.24.0-1_amd64.deb" \
  -F "distribution=jammy" \
  http://localhost:8000/upload/
# On an internet-connected machine
dnf download nginx --destdir=/tmp/
scp /tmp/nginx-*.rpm repod-server:/tmp/

# On the Repod server β€” upload the .rpm
TOKEN=$(curl -s ... | jq -r .access_token)
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -F "file=@/tmp/nginx-1.24.0-1.el9.ngx.x86_64.rpm" \
  -F "distribution=almalinux9" \
  http://localhost:8000/upload/

Troubleshooting

Sync returns 0 packages

docker compose logs backend-api | grep -i "sync\|error\|index"

APT: The remote Release file cannot be fetched β€” check URL, network access, and whether the source requires authentication.

RPM: The repodata/repomd.xml is unreachable or the SQLite index database is locked.

Fix a locked SQLite index:

docker exec -u root backend-api \
  chown appuser:appuser /repos/package-index/packages.db
docker compose restart backend-api

Import fails with 404 Not Found

The package version in the catalog no longer exists on the remote server (removed upstream). Re-sync the index and select a current version.

Package stuck in pending_review

The CVE scan found findings matching the review policy. Go to Security β†’ Review queue, examine the findings, and approve or reject.

GPG key retrieval failed during import

The remote package is signed with a key Repod doesn't trust. This is expected for external packages. The package still passes if the GPG check is configured as soft (default behavior).


Scheduled synchronization

To keep the index fresh automatically, configure a scheduled sync in Settings β†’ Scheduled tasks or use a cron job:

# Sync all sources daily at 02:00
0 2 * * * curl -sf -X POST \
  -H "Authorization: Bearer $REPOD_TOKEN" \
  http://localhost:8000/import/sources/1/sync >> /var/log/repod-sync.log 2>&1

Replace $REPOD_TOKEN with a long-lived API token created for the automation user (admin or maintainer role).