K3s Kubernetes Setup: Lightweight Container Orchestration
Single and multi-node Kubernetes cluster setup with K3s, kubectl usage, creating deployments and services, application management with Helm. Kubernetes guide for VPS environments.
Private Docker Registry setup, secure registry with TLS, authentication, image push/pull operations, enterprise registry with Harbor, and CI/CD integration.
While public registries like Docker Hub are convenient, hosting your own registry for private images offers advantages in security, speed, and cost. This guide covers options from simple Docker Registry to enterprise Harbor.
| Advantage | Description |
|---|---|
| Security | Private images not exposed publicly |
| Speed | Fast pull/push on local network |
| Cost | No Docker Hub rate limits |
| Control | Full image lifecycle control |
| Compliance | Data localization requirements |
# Start registry container
docker run -d \
--name registry \
--restart always \
-p 5000:5000 \
-v registry-data:/var/lib/registry \
registry:2
# Verify registry is running
curl http://localhost:5000/v2/
# Output: {}
# Tag image
docker tag myapp:latest localhost:5000/myapp:latest
# Push to registry
docker push localhost:5000/myapp:latest
# Pull from registry
docker pull localhost:5000/myapp:latest
# List images in registry
curl http://localhost:5000/v2/_catalog
# Output: {"repositories":["myapp"]}
# List image tags
curl http://localhost:5000/v2/myapp/tags/list
# Create certificate directory
mkdir -p /etc/docker/certs/registry
# Create self-signed certificate
openssl req -newkey rsa:4096 -nodes -sha256 \
-keyout /etc/docker/certs/registry/domain.key \
-x509 -days 365 \
-out /etc/docker/certs/registry/domain.crt \
-subj "/CN=registry.example.com" \
-addext "subjectAltName=DNS:registry.example.com,IP:192.168.1.100"
# Add certificate to Docker's trusted certificates
mkdir -p /etc/docker/certs.d/registry.example.com:5000
cp /etc/docker/certs/registry/domain.crt \
/etc/docker/certs.d/registry.example.com:5000/ca.crt
# Restart Docker
systemctl restart docker
docker run -d \
--name registry \
--restart always \
-p 5000:5000 \
-v registry-data:/var/lib/registry \
-v /etc/docker/certs/registry:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry:2
# Create htpasswd file
mkdir -p /etc/docker/registry/auth
docker run --rm \
--entrypoint htpasswd \
httpd:2 -Bbn admin StrongPass123! > /etc/docker/registry/auth/htpasswd
# Start registry with authentication
docker run -d \
--name registry \
--restart always \
-p 5000:5000 \
-v registry-data:/var/lib/registry \
-v /etc/docker/certs/registry:/certs \
-v /etc/docker/registry/auth:/auth \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
-e REGISTRY_AUTH=htpasswd \
-e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:2
# Login
docker login registry.example.com:5000
# registry-compose.yml
version: '3.8'
services:
registry:
image: registry:2
restart: always
ports:
- "5000:5000"
environment:
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
REGISTRY_HTTP_TLS_KEY: /certs/domain.key
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_STORAGE_DELETE_ENABLED: "true"
volumes:
- registry-data:/var/lib/registry
- ./certs:/certs:ro
- ./auth:/auth:ro
registry-ui:
image: joxit/docker-registry-ui:latest
restart: always
ports:
- "8080:80"
environment:
- REGISTRY_TITLE=REXE Private Registry
- REGISTRY_URL=https://registry.example.com:5000
- SINGLE_REGISTRY=true
depends_on:
- registry
volumes:
registry-data:
Harbor is a CNCF-supported enterprise-grade container registry.
# Download Harbor installer
wget https://github.com/goharbor/harbor/releases/download/v2.9.0/harbor-online-installer-v2.9.0.tgz
tar xzf harbor-online-installer-v2.9.0.tgz
cd harbor
# Edit configuration
cp harbor.yml.tmpl harbor.yml
nano harbor.yml
# harbor.yml (basic settings)
hostname: registry.example.com
https:
port: 443
certificate: /etc/harbor/certs/domain.crt
private_key: /etc/harbor/certs/domain.key
harbor_admin_password: StrongPass123!
data_volume: /data/harbor
# Install Harbor
./install.sh
# Login to Harbor
docker login registry.example.com
# User: admin
# Password: StrongPass123!
# Create project and push image
docker tag myapp:latest registry.example.com/myproject/myapp:latest
docker push registry.example.com/myproject/myapp:latest
# Clean up unused layers
docker exec registry registry garbage-collect \
/etc/docker/registry/config.yml
# Dry-run (check before deleting)
docker exec registry registry garbage-collect \
--dry-run \
/etc/docker/registry/config.yml
# Delete image (via API)
curl -X DELETE \
https://registry.example.com:5000/v2/myapp/manifests/DIGEST \
--cacert /etc/docker/certs/registry/domain.crt \
-u admin:StrongPass123!
# .github/workflows/build-push.yml
name: Build and Push
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Private Registry
uses: docker/login-action@v3
with:
registry: registry.example.com:5000
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build and Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: registry.example.com:5000/myapp:${{ github.sha }}
# Add registry credentials as secret
kubectl create secret docker-registry registry-secret \
--docker-server=registry.example.com:5000 \
--docker-username=admin \
--docker-password=StrongPass123! \
--docker-email=admin@example.com
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
imagePullSecrets:
- name: registry-secret
containers:
- name: myapp
image: registry.example.com:5000/myapp:latest
A private Docker Registry is essential for image security and management in enterprise environments. Choose registry:2 for simple use cases, Harbor for enterprise features (RBAC, security scanning, replication). With a private registry on REXE servers, you can securely store your images and integrate them into your CI/CD pipelines.
Several important reasons: 1) Security — your private images aren't publicly exposed, 2) Speed — pull/push is much faster on a local network, 3) No rate limits — Docker Hub free accounts have hourly pull limits, 4) Data control — your images stay on your own server, 5) Compliance — some industries require data localization.
If you're using an HTTP (non-TLS) registry, you need to tell Docker to trust it as an insecure registry. Add {"insecure-registries": ["registry.example.com:5000"]} to /etc/docker/daemon.json and restart Docker. Using TLS in production is strongly recommended.
Two steps: 1) First delete images via API (DELETE /v2/IMAGE/manifests/DIGEST), 2) Then run garbage collection (docker exec registry registry garbage-collect /etc/docker/registry/config.yml). Without garbage collection, only the manifest is deleted — layers remain on disk.
registry:2 is a simple, lightweight image store. Harbor offers enterprise features: RBAC (role-based access control), vulnerability scanning (Trivy integration), image replication, webhooks, audit logs, and a web UI. registry:2 is recommended for small teams, Harbor for large organizations.
Kubernetes needs registry credentials. Create credentials as a secret with kubectl create secret docker-registry and add imagePullSecrets to your deployment. Alternatively, add imagePullSecrets to the service account to make it valid across the namespace.
Single and multi-node Kubernetes cluster setup with K3s, kubectl usage, creating deployments and services, application management with Helm. Kubernetes guide for VPS environments.
Docker volume types (named, bind mount, tmpfs), volume management, Docker network modes (bridge, host, overlay), creating custom networks and container communication. Comprehensive guide.
Docker log drivers, docker logs command, Kubernetes pod logs, centralized log collection (Loki, ELK Stack), log rotation, and production log management best practices.