Pushing Images
Learn how to distribute Docker images to your cluster machines and external registries.
Overview
After building an image, it exists only on your local machine. To deploy it to your cluster, the image must be pushed to cluster machines.
Uncloud provides two ways to push images:
- Push to cluster machines (
--push) - Direct transfer to cluster machines - Push to external registry (
--push-registry) - Upload to Docker Hub, GitHub Container Registry, etc.
Push to Cluster Machines
The most common workflow: build locally and push directly to your cluster.
Basic Push
# Build and push in one command
uc build --push
This:
- Builds images locally
- Connects to your cluster
- Pushes images to target machines via gRPC
Automatic Push with Deploy
When using uc deploy, images are automatically pushed:
uc deploy
This does:
- Build images locally
- Push images to cluster
- Deploy containers
Equivalent to:
uc build
uc build --push # (happens automatically)
uc deploy --no-build
Target Specific Machines
Push to specific machines:
# Push to specific machines
uc build --push -m machine1,machine2
# Or
uc build --push --machines machine1,machine2
Service-specific Machine Targeting
Using x-machines in compose.yaml:
services:
web:
build: .
image: myapp:latest
x-machines:
- machine1
- machine2
deploy:
replicas: 2
uc build --push
Images are pushed only to machine1 and machine2 (as specified in x-machines).
Push All Services
Push all built images:
uc build --push
Push Specific Services
Push only certain service images:
# Build and push only frontend
uc build frontend --push
# Build and push frontend and API
uc build frontend api --push
Push to External Registries
Push images to Docker Hub, GitHub Container Registry, or private registries.
Docker Hub
Tag your image for Docker Hub:
services:
web:
build: .
image: username/myapp:v1.2.3
Authenticate:
docker login
Build and push:
uc build --push-registry
Your image is now available at docker.io/username/myapp:v1.2.3.
GitHub Container Registry
Tag for GitHub:
services:
web:
build: .
image: ghcr.io/username/myapp:v1.2.3
Authenticate:
echo $GITHUB_TOKEN | docker login ghcr.io -u username --password-stdin
Push:
uc build --push-registry
Private Registry
Tag for your registry:
services:
web:
build: .
image: registry.example.com/myapp:v1.2.3
Authenticate:
docker login registry.example.com
Push:
uc build --push-registry
Push Strategies
Strategy 1: Direct Cluster Push (Default)
Best for most use cases:
uc build --push
Pros:
- Fast (direct machine-to-machine transfer)
- No external dependencies
- Works behind firewalls
Cons:
- Images only available in one cluster
- Requires SSH access to machines
Strategy 2: Registry-based
Push to registry, pull from cluster:
# Build and push to registry
uc build --push-registry
# Update compose.yaml to use registry image
# Deploy (pulls from registry)
uc deploy --no-build
Pros:
- Images available to multiple clusters
- Version history in registry
- Can deploy from any machine
Cons:
- Slower (upload + download)
- Requires registry setup
- Potential bandwidth costs
Strategy 3: Hybrid
Push to both cluster and registry:
# Build once
uc build
# Push to cluster for immediate deployment
uc build --push
# Also push to registry for backup/sharing
uc build --push-registry
Pros:
- Fast deployment
- Images backed up in registry
- Can rollback from registry
Cons:
- Double push time
- More bandwidth usage
Understanding Image Distribution
How Cluster Push Works
Your Machine
↓ (build)
Local Docker daemon
↓ (save image as tar)
SSH/gRPC connection
↓ (transfer tar over network)
Cluster machine
↓ (load tar into Docker)
Docker daemon on machine
Parallel Push
For multiple machines, Uncloud pushes in parallel:
uc build --push
Your Machine
↓
├─→ machine1 (parallel)
├─→ machine2 (parallel)
└─→ machine3 (parallel)
Progress Tracking
Watch push progress:
Pushing images to cluster...
✓ Pushed myapp:latest to machine-1 (2.3 MB in 1.2s)
✓ Pushed myapp:latest to machine-2 (2.3 MB in 1.5s)
✓ Pushed myapp:latest to machine-3 (2.3 MB in 1.8s)
Image Tagging
Semantic Versioning
Tag images with version numbers:
services:
web:
build: .
image: myapp:1.2.3
uc build --push
Git-based Tagging
Use git commit SHA:
services:
web:
build: .
image: myapp:${GIT_SHA}
GIT_SHA=$(git rev-parse --short HEAD)
uc build --push
Environment-based Tagging
Different tags for different environments:
services:
web:
build: .
image: myapp:${ENV:-dev}
# Development
uc build --push
# Production
ENV=prod uc build --push
Multiple Tags
Tag the same image multiple times:
services:
web:
build: .
image: myapp:1.2.3
x-tags:
- myapp:latest
- myapp:stable
After building:
docker tag myapp:1.2.3 myapp:latest
docker tag myapp:1.2.3 myapp:stable
uc build --push
CI/CD Integration
GitHub Actions
name: Build and Push
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build image
run: uc build --build-arg VERSION=${{ github.sha }}
- name: Push to cluster
run: uc build --push
- name: Deploy
run: uc deploy --no-build --yes
GitLab CI
stages:
- build
- deploy
build:
stage: build
script:
- uc build --build-arg VERSION=$CI_COMMIT_SHA
- uc build --push-registry
deploy:
stage: deploy
script:
- uc deploy --no-build --yes
only:
- main
Push to Registry in CI
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
run: uc build --push-registry
Optimizing Push Performance
Reduce Image Size
Smaller images push faster:
# Use alpine base images
FROM node:20-alpine
# Multi-stage builds
FROM node:20 AS builder
RUN npm run build
FROM node:20-alpine
COPY --from=builder /app/dist /app/dist
Use Build Cache
Leverage Docker layer caching:
# Dependencies (cached unless package.json changes)
COPY package*.json ./
RUN npm install
# Source code (only this rebuilds on changes)
COPY . .
.dockerignore
Exclude unnecessary files from build context:
node_modules
.git
*.log
.env*
dist/
build/
Compress Images
Multi-stage builds automatically reduce image size:
FROM golang:1.21 AS builder
COPY . .
RUN go build -o app
FROM alpine:latest
COPY --from=builder /app /app
Final image only contains compiled binary, not build tools.
Troubleshooting
Push Fails to Cluster
Check connectivity:
# Verify SSH access
ssh user@machine1
# Check Uncloud daemon
ssh user@machine1 'systemctl status uncloud'
Push to Registry Fails
Verify authentication:
docker login
docker push myapp:latest
Check credentials:
cat ~/.docker/config.json
Image Too Large
If push is slow due to large images:
- Use multi-stage builds
- Use smaller base images (alpine)
- Add
.dockerignore - Remove unnecessary files in Dockerfile
Out of Disk Space on Machines
Clean up old images on cluster machines:
ssh user@machine1 'docker image prune -a'
Or configure automatic cleanup.
Registry Rate Limits
Docker Hub has rate limits for pulls. Solutions:
- Authenticate (higher limits for authenticated users)
- Use Docker Hub Pro/Team
- Use alternative registries (GitHub, GitLab)
Push Hangs
If push seems stuck:
- Check network connectivity
- Check machine disk space
- Verify Docker daemon is running on machines
- Check firewall rules
Security Considerations
Private Registries
Use authentication for private registries:
docker login registry.example.com
Configure registry credentials on all machines that need to pull images.
Image Scanning
Scan images for vulnerabilities before pushing:
# Build image
uc build
# Scan with Docker Scout
docker scout cves myapp:latest
# If safe, push
uc build --push
Signed Images
Sign images for verification:
# Enable Docker Content Trust
export DOCKER_CONTENT_TRUST=1
# Push will sign image
uc build --push-registry
Best Practices
- Version your images with semantic versioning
- Use specific tags in production (not
:latest) - Push to registry for backup and sharing
- Optimize image size for faster pushes
- Scan images for vulnerabilities before pushing
- Use multi-arch images for heterogeneous clusters
- Tag with git SHA for traceability
- Clean up old images regularly
Next Steps
- Build Options - Optimize your builds for faster pushes
- When to Build - Learn different build and push workflows
- Deploy from Dockerfile - See the complete build-push-deploy cycle