Local Docker Registry & Multi-Service Build/Deploy Guide
-
Jason Yang - 25 Mar, 2026
- Views —
Using GCP Artifact Registry incurs small costs, and Docker Hub allows only one private repository on the free tier.
This guide explains how to build Docker images on a development PC, push them to a local registry on a LAN, and deploy them to a test server without cloud costs.
Background
Previously, the test environment built everything directly on the test laptop:
[Old Approach]
Test Laptop: git pull → mvn build → docker build → docker run
Problems:
- Requires full build environment (JDK, Maven, Node.js, etc.) on the test server
- Slow build times
- Risk of inconsistent environments
Improved approach:
[Improved Approach]
Dev PC: code change → build-push.sh → push to local registry
Test Server: docker-compose pull → docker-compose up
Environment Setup
| Role | Machine | OS |
|---|---|---|
| Dev PC | 750XDA | Ubuntu |
| Test Server | GU502DU | Ubuntu |
- Test server IP:
192.168.1.144 - Registry port:
5000
Step 1: Install Docker Registry on Test Server
Create a dedicated user
sudo useradd -m -s /bin/bash dkr
sudo usermod -aG docker dkr
sudo usermod -aG sudo dkr
Open firewall port
sudo ufw allow 5000/tcp
sudo ufw status
Run registry container
su - dkr
docker run -d \
-p 5000:5000 \
--restart=always \
--name registry \
registry:2
Verify
curl http://192.168.1.144:5000/v2/_catalog
Expected:
{"repositories":[]}
Step 2: Configure Insecure Registry (Dev PC)
sudo tee /etc/docker/daemon.json <<EOF
{
"insecure-registries": ["192.168.1.144:5000"]
}
EOF
sudo systemctl restart docker
Step 3: Build & Push Services
Java / Spring Boot
./mvnw clean package -DskipTests
docker build -t 192.168.1.144:5000/user-service:latest .
docker push 192.168.1.144:5000/user-service:latest
Frontend (Next.js)
./docker-build.sh
docker tag frontend:latest 192.168.1.144:5000/frontend:latest
docker push 192.168.1.144:5000/frontend:latest
Python / FastAPI (with BuildKit Secret)
DOCKER_BUILDKIT=1 docker build \
--secret id=gcp_sa_key,src=/path/to/key.json \
-t 192.168.1.144:5000/chat-service:latest \
.
docker push 192.168.1.144:5000/chat-service:latest
Step 4: build-push.sh Script
Example:
#!/bin/bash
set -e
REGISTRY="192.168.1.144:5000"
IMAGE="user-service"
./mvnw clean package -DskipTests
docker build -t ${REGISTRY}/${IMAGE}:latest .
docker push ${REGISTRY}/${IMAGE}:latest
Step 5: Verify Registry
curl http://192.168.1.144:5000/v2/_catalog
Docker Hub vs Local Registry
| Feature | Local Registry | Docker Hub |
|---|---|---|
| Cost | Free | Limited |
| Speed | Fast (LAN) | Slower |
| Security | Internal | External |
Automatic Deployment with Watchtower
Until now, deployment required manually running:
docker-compose pull
docker-compose up -d
We can automate this using Watchtower.
What is Watchtower?
Watchtower monitors running containers and automatically updates them when a new image is available.
Install Watchtower
On the test server:
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--interval 30
- Checks for updates every 30 seconds
Optional: Enable per-service updates
services:
user-service:
image: 192.168.1.144:5000/user-service:latest
labels:
- "com.centurylinklabs.watchtower.enable=true"
Run Watchtower with:
--label-enable
How It Works
Dev PC → build-push.sh → push to registry
Test Server:
Watchtower detects new image
→ pulls image
→ restarts container automatically
Advantages
- No manual deployment needed
- Near real-time updates
- Simple setup
- Works without full CI/CD pipeline
Considerations
- Using
latestmay cause caching issues
→ Prefer versioned tags (e.g.,:20260324-1) - Containers are restarted automatically
→ Not zero-downtime by default - For production:
- Add validation
- Add rollback strategy
Final Result
With this setup: 👉 Push from Dev PC automatically updates the test server.
Simple, fast, and cost-free deployment inside your LAN.