https://github.com/cyr-ius/portalcrane
Portalcrane is a modern web application for managing Docker Registry (CNCF Distribution). It provides a beautiful, intuitive interface to browse, search, manage images and tags, with a staging pipeline including antivirus scanning.
https://github.com/cyr-ius/portalcrane
distribution docker docker-image ui
Last synced: 4 months ago
JSON representation
Portalcrane is a modern web application for managing Docker Registry (CNCF Distribution). It provides a beautiful, intuitive interface to browse, search, manage images and tags, with a staging pipeline including antivirus scanning.
- Host: GitHub
- URL: https://github.com/cyr-ius/portalcrane
- Owner: cyr-ius
- License: mit
- Created: 2026-02-21T16:26:10.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-03-23T19:20:51.000Z (4 months ago)
- Last Synced: 2026-03-24T05:33:32.187Z (4 months ago)
- Topics: distribution, docker, docker-image, ui
- Language: Python
- Homepage:
- Size: 1.7 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# π³ Portalcrane
**Portalcrane** is a self-hosted Docker registry manager.
It offers a modern and intuitive interface for browsing, searching, and managing images and tags,
with a preparation process that includes vulnerability scanning.
It also allows you to declare external registries and perform transfers between them.
Portalcrane's internal registry allows you to organize images into directories. An RBAC model allows you to control image usage.

---
## Table of Contents
- [Features](#features)
- [Architecture](#architecture)
- [Prerequisites](#prerequisites)
- [Quick Start](#quick-start)
- [Ports](#ports)
- [Healthcheck](#healthcheck)
- [Security Notes](#security-notes)
- [Environment Variables](#environment-variables)
- [Dashboard](#dashboard)
- [User Management](#user-management)
- [Folder-Based Access Control](#folder-based-access-control)
- [Staging Pipeline](#staging-pipeline)
- [External Registries & Sync](#external-registries--sync)
- [Data Persistence](#data-persistence)
- [Development](#development)
- [CI / CD](#ci--cd)
- [Screenshots](#screenshots)
- [License](#license)
## Features
- π¨ Modern UI with light / dark / auto themes
- π Local authentication (admin + per-user accounts) with optional OIDC support
- π₯ Multi-user management with granular pull / push permissions
- π Folder-based access control (per-user pull/push permissions on image namespaces)
- π¦ Browse, search, and paginate images and tags
- ποΈ Delete images or individual tags
- π·οΈ Retag: add new tags to existing images
- π Staging pipeline: Search Docker Hub β Pull β Trivy CVE scan (optional) β Push to registry
- π Dashboard with live stats (image count, disk usage, largest image, user & admin counts)
- π Advanced mode: detailed image metadata (layers, labels, env vars, architectureβ¦)
- π External registries: CRUD management + connectivity test
- π Sync: push local images to external registries (full or per-image)
- π‘ Syslog support in the Network tab
- π Audit logs: full history of API operations
- π Registry proxy with authentication enforcement
- βΉοΈ About panel with version check against the latest GitHub release
- π³ Single-container deployment (frontend + backend + registry in one image)
---
## Architecture
| Layer | Technology |
| -------------------------- | ------------------------------------------------------------------- |
| **Frontend** | Angular 21 β Signals, Signal Forms, Zoneless, standalone components |
| **Styling** | Bootstrap 5 + Bootstrap Icons |
| **Backend** | FastAPI + Python 3.14 (fully async) |
| **Validation** | Pydantic v2 |
| **Registry** | Distribution (CNCF) v3 embedded |
| **Vulnerability scanning** | Trivy (embedded) |
| **Image transfer** | skopeo (daemon-less pull/push/copy) |
| **Container** | Single image β supervisord orchestrates all processes |
| **Platforms** | `linux/amd64`, `linux/arm64` (Raspberry Pi, Apple Silicon) |
---
## Prerequisites
- Docker 24+ (or compatible)
- Docker Compose v2 (optional, for compose usage)
## Quick Start
### Docker CLI
```bash
docker run -d \
--name portalcrane \
-p 8080:8080 \
-e ADMIN_USERNAME=admin \
-e ADMIN_PASSWORD=yourpassword \
-e SECRET_KEY=your-secret-key \
-v /portalcrane_data:/var/lib/portalcrane \
cyrius44/portalcrane:latest
```
Open **http://localhost:8080** and log in with your admin credentials.
### Docker Compose
```yaml
services:
portalcrane:
image: cyrius44/portalcrane:latest
container_name: portalcrane
ports:
- "8080:8080"
environment:
- ADMIN_USERNAME=admin
- ADMIN_PASSWORD=changeme
- SECRET_KEY=your-secret-key
volumes:
- portalcrane_data:/var/lib/portalcrane
restart: unless-stopped
volumes:
portalcrane_data:
```
### Usage
Access the web interface (`http(s)://:8080`) to control, pull or push your images
or directly with Docker commands via the registry proxy.
```bash
docker login :8080
docker pull :
docker push :
docker logout
```
For full access without authentication, set the REGISTRY_PROXY_AUTH_ENABLED variable to `false`.
If you are using the dev stack and want direct registry access, use `:5000`.
### Docker Compose (dev stack, from this repo)
This stack builds the local image and also starts a dedicated registry on port `5000`.
Registry data is stored in the `registry_data` volume.
```bash
docker compose up -d
```
---
## Ports
- `8080` β Portalcrane UI + API + registry proxy
- `5000` β Docker registry (dev stack only)
## Healthcheck
`GET /api/health` returns a JSON status payload.
## Security Notes
- `SECRET_KEY` must be set to a non-default value or the app will refuse to start.
- Always change `ADMIN_PASSWORD` on first launch.
- If you expose the UI publicly, enable HTTPS at the reverse proxy level.
## Environment Variables
### Authentication
| Variable | Description | Default |
| ---------------- | --------------------------------------------- | -------------------------------------- |
| `ADMIN_USERNAME` | Built-in admin username | `admin` |
| `ADMIN_PASSWORD` | Built-in admin password | `changeme` |
| `SECRET_KEY` | JWT signing secret β **change in production** | `change-this-secret-key-in-production` |
### Registry
| Variable | Description | Default |
| ----------------------------- | --------------------------------------------------- | ------- |
| `REGISTRY_PROXY_AUTH_ENABLED` | Enforce authentication on the `/v2/` registry proxy | `true` |
### OIDC (optional)
| Variable | Description | Default |
| ------------------------------- | ------------------------ | ---------------------- |
| `OIDC_ENABLED` | Enable OIDC login | `false` |
| `OIDC_ISSUER` | OIDC issuer URL | β |
| `OIDC_CLIENT_ID` | OIDC client ID | β |
| `OIDC_CLIENT_SECRET` | OIDC client secret | β |
| `OIDC_REDIRECT_URI` | OIDC redirect URI | β |
| `OIDC_POST_LOGOUT_REDIRECT_URI` | Post-logout redirect URI | β |
| `OIDC_RESPONSE_TYPE` | OIDC response type | `code` |
| `OIDC_SCOPE` | OIDC scopes | `openid profile email` |
These values ββcan be overridden by the UI.
### Vulnerability Scanning (Trivy)
| Variable | Description | Default |
| ---------------------- | --------------------------------------------- | --------------- |
| `VULN_SCAN_ENABLED` | Enable Trivy CVE scan in the staging pipeline | `true` |
| `VULN_SCAN_SEVERITIES` | Blocking severity levels (comma-separated) | `CRITICAL,HIGH` |
| `VULN_IGNORE_UNFIXED` | Ignore CVEs with no available fix | `false` |
| `VULN_SCAN_TIMEOUT` | Trivy scan timeout | `5m` |
These values ββcan be overridden by the UI.
### HTTP Proxy (optional)
| Variable | Description | Default |
| ------------- | ------------------------------ | --------------------- |
| `HTTP_PROXY` | HTTP proxy URL | β |
| `HTTPS_PROXY` | HTTPS proxy URL | β |
| `NO_PROXY` | Comma-separated no-proxy hosts | `localhost,127.0.0.1` |
### Logging
| Variable | Description | Default |
| ------------------ | ----------------------------------------------- | ------- |
| `LOG_LEVEL` | Log level (`DEBUG`, `INFO`, `WARNING`, `ERROR`) | `INFO` |
| `AUDIT_MAX_EVENTS` | Maximum number of audit events retained on disk | `100` |
### Storage (debug)
| Variable | Description | Default |
| ---------- | ----------------------------------------------- | -------------------- |
| `DATA_DIR` | Base data directory inside the container | `/var/lib/portalcrane` |
---
## Dashboard
The dashboard displays a real-time overview of the registry:
- **Total Images** β number of repositories in the registry
- **Registry Size** β total stored data (human-readable)
- **Disk Usage** β disk usage percentage with a colour-coded progress bar
- **Users** β total account count and number of administrators (admin count turns red when > 5)
It also exposes quick actions (browse images, pull from Docker Hub) and maintenance operations (Garbage Collection).
---
## User Management
Portalcrane supports two types of accounts:
- **Environment admin** β the built-in account defined via `ADMIN_USERNAME` / `ADMIN_PASSWORD`. It cannot be deleted or modified through the UI.
- **Local users** β created via the **Settings β Accounts** panel. Each user can be assigned:
- Admin role (full access)
- Pull permission (read images from the registry)
- Push permission (write / delete images in the registry)
---
## Folder-Based Access Control
Administrators can define **folders** (image namespace prefixes, e.g. `production/`) and assign per-user pull/push permissions on each folder. Non-admin users can only access images whose path matches a folder they have been granted access to.
---
## Staging Pipeline
1. **Search** Docker Hub for an image
2. **Select** an image and a tag
3. **Pull** β skopeo downloads the image as an OCI layout (no Docker daemon required)
4. **Scan** β Trivy analyses the image for CVEs (optional, configurable severity thresholds)
5. **Push** β skopeo copies the OCI layout to the private registry under a chosen name and tag
---
## External Registries & Sync
- Add external Docker-compatible registries (Docker Hub, GHCR, Quay, self-hostedβ¦)
- Test connectivity and authentication directly from the UI
- Sync local images to an external registry (full registry or per-image/folder)
---
## Data Persistence
All persistent data is stored in **/var/lib/portalcrane** inside the container. Mount a volume to this path to retain data across container restarts:
```
-v /portalcrane_data:/var/lib/portalcrane
```
Stored data includes: local users, OIDC configuration, folder permissions, external registries, audit logs, and the registry image data itself.
---
## Development
### Backend
```bash
cd backend
pip install -r requirements.txt
uvicorn main:app --reload --port 8000
```
### Frontend
```bash
cd frontend
npm install
ng serve --port 4200 --proxy-config proxy.conf.json
```
---
## CI / CD
The GitHub Actions workflow (`.github/workflows/docker-publish.yml`) automatically:
1. Builds a image (`linux/amd64`) on every push to `main` and on version tags
2. Publishes to **Docker Hub** (`cyrius44/portalcrane`) and **GHCR** (`ghcr.io/cyr-ius/portalcrane`)
3. Updates the Docker Hub description from this README
Image tags follow semantic versioning: `latest`, `edge`, `X`, `X.Y`, `X.Y.Z`, `sha-`.
---
## Screenshots



---
## License
MIT β see [LICENSE](LICENSE) for details.
## About
Author: [@cyr-ius](https://github.com/cyr-ius) β Sponsor: [GitHub Sponsors](https://github.com/sponsors/cyr-ius)