This directory contains all configuration files needed to run Harbor locally for development.
Containerized with Hot Reload: All Harbor services run in Docker containers with Air hot reload. Services communicate via Docker network - no localhost issues.
# Start full dev environment (includes OpenAPI UI)
task dev:up
# Start infrastructure only (for native development)
task dev:infra:upAll services run in containers:
- Core - Harbor API with Air hot reload (port 8080, Delve 2345)
- JobService - Background jobs with Air hot reload (port 8888, Delve 2346)
- RegistryCtl - Registry controller with Air hot reload (port 8085, Delve 2347)
- Trivy Adapter - Vulnerability scanner (port 8081)
- PostgreSQL - Database (port 5432)
- Redis/Valkey - Cache/queue (port 6379)
- Distribution - Docker Registry (port 50000)
┌────────────────────────────────────────┐
│ Docker Network │
│ │
┌──────────┐ │ ┌──────┐ ┌───────────┐ ┌───────┐ │
│ Portal │◄───────┼─►│ Core │◄─│ JobService│◄─│ Trivy │ │
│ :4200 │ │ │:8080 │ │ :8888 │ │ :8081 │ │
└──────────┘ │ └──┬───┘ └─────┬─────┘ └───────┘ │
(native) │ │ │ │
│ ┌──▼────────────▼──┐ │
│ │ PostgreSQL │ │
│ │ :5432 │ │
│ └──────────────────┘ │
│ ┌──────────────────┐ │
│ │ Redis │ │
│ │ :6379 │ │
│ └──────────────────┘ │
│ ┌──────────────────┐ │
│ │ Registry │ │
│ │ :50000 │ │
│ └──────────────────┘ │
└────────────────────────────────────────┘
devenv/
├── docker-compose.yml # All services (infra + backend + trivy)
├── air.core.toml # Air hot-reload config for Core
├── air.jobservice.toml # Air hot-reload config for JobService
├── air.registryctl.toml # Air hot-reload config for RegistryCtl
├── registry.passwd # Registry HTTP basic auth credentials
├── token_service_key.pem # Generated RSA key for token signing
└── README.md # This file
config/ # Canonical service configs (shared by dev + compose)
├── jobservice.yml # JobService configuration
├── registry.yml # Docker Registry config
├── registryctl.yml # RegistryCtl configuration
├── nginx/ # Reverse proxy + TLS (compose only)
└── portal/nginx.conf # Baked into portal image at build time
dockerfile/
└── dev.core.dockerfile # Dev image with Go, Air, and Delve
task dev:up # Start everything (Core, JobService, Trivy, Portal)
task dev:up SKIP_TRIVY=true # Start without Trivy
task dev:status # Show running containers
task dev:logs # View all container logs
task dev:clean # Stop everything and remove volumestask dev:backend:up # Start Core + JobService containers
task dev:backend:down # Stop backend containers
task dev:backend:logs # View backend logs
task dev:backend:logs:<service> # View logs for core, jobservice, or registryctl
task dev:backend:restart # Restart backend containerstask dev:infra:up # Start PostgreSQL, Redis, Registry
task dev:infra:down # Stop infrastructure
task dev:infra:remove # Remove with volumestask dev:frontend:native # Start Angular with HMR (runs natively)
task dev:frontend:build # Build frontend for production
task dev:frontend:test # Run frontend tests in watch mode
task dev:frontend:test:once # Run frontend tests oncetask dev:db:shell # Open PostgreSQL shell
task dev:db:migrate # Run migrations
task dev:db:reset # Reset to clean state (shows [y/N] prompt)task dev:gen:private-key # Generate RSA private key for token signing
task dev:info # Show current SLOT and port assignments
task dev:logs # View all container logstask build # Build all binaries
task build:gen-apis # Generate API server code from OpenAPI spec
task test # Run all tests
task test:lint # Run Go linters
task test:unit # Run Go unit tests
task test:quick # Quick validation (fast checks only)Code changes are automatically detected and rebuilt:
- Go files: Air watches
src/and rebuilds (~3 seconds) - Frontend: Angular HMR (< 1 second once initial compile is complete)
- OpenAPI UI: Swagger UI assets are built in the background during portal startup
Volume mounts with VirtioFS (Docker Desktop 4.x+) provide fast file system event propagation.
All Go services run under Delve with --continue (no need to wait for debugger attach). Host port mappings are commented out by default to save resources — uncomment them in docker-compose.yml when needed:
- Core: container
:2345→ host${PORT_DEBUG_CORE:-2345}(commented out) - JobService: container
:2346→ host:2346(commented out) - RegistryCtl: container
:2347→ host${PORT_DEBUG_REGISTRYCTL:-2347}(active)
To enable Core/JobService debugging, uncomment the port mappings in devenv/docker-compose.yml and restart.
| Service | URL |
|---|---|
| Portal | http://localhost:4200/ |
| API | http://localhost:8080/api/v2.0 |
| JobService | http://localhost:8888 |
| Trivy | http://localhost:8081 |
| Registry | http://localhost:50000 |
- Harbor Admin:
admin/Harbor12345 - PostgreSQL:
postgres/root123
Once the dev environment is running, you can push container images to test the full workflow.
Add localhost:8080 to your Docker daemon's insecure registries. In Docker Desktop, go to Settings > Docker Engine and add:
{
"insecure-registries": [
"localhost:8080"
]
}Restart Docker Desktop after making this change.
# 1. Log in to Harbor (default admin credentials)
docker login localhost:8080 -u admin -p Harbor12345
# 2. Tag an image for Harbor (images go into projects, "library" is the default)
docker pull hello-world
docker tag hello-world:latest localhost:8080/library/hello-world:latest
# 3. Push the image
docker push localhost:8080/library/hello-world:latest
# 4. Verify via API
curl -s -u admin:Harbor12345 http://localhost:8080/api/v2.0/projects/library/repositories | python3 -m json.tool
# 5. Pull it back
docker pull localhost:8080/library/hello-world:latest- The
libraryproject is created automatically during database migration and is public by default. - All images must be pushed to a project:
localhost:8080/<project>/<image>:<tag>. - You can create additional projects via the Portal at
http://localhost:4200or the API. - If using a non-default SLOT (e.g.,
SLOT=1), the Core port shifts accordingly (e.g.,8180). Update the insecure-registries entry to match. - Pushing via Portal port (4200) does not work. Harbor Core sets
EXT_ENDPOINTto its internal Docker hostname (core:8080), so theWWW-Authenticatetoken realm in/v2/responses points tocore:8080, which the host Docker client cannot resolve. Always push directly to the Core port (8080).
The first run downloads Go modules and builds the dev image. Subsequent runs use cached layers.
The Go module cache is persisted in a Docker volume (go-mod-cache). If you see module errors, try:
task dev:clean
task dev:upCheck for existing processes:
lsof -i :8080 # Core
lsof -i :8888 # JobService
lsof -i :4200 # Portal