So, you have a legacy Java-based application running on aging virtual machines, and you want to move it into containers and deploy it on Kubernetes. You're not alone.
We’re helping a customer do exactly that: transitioning a sprawling, service-oriented Java application into a modern, containerized environment orchestrated by Kubernetes. This is more than a technical refresh; it's a full operational and architectural shift.
If you're planning a similar journey, here are the high-level steps we're following, including insights from our DevOps transformation work and containerization readiness checklist.
Step 1: Understand the Existing Application
Before you write a single line of Dockerfile, you need a full understanding of how each service works.
Document:
-
How each service starts, runs, and shuts down
-
All runtime dependencies (files, databases, queues, other services)
-
Communication methods (HTTP, gRPC, shared libraries, IPs, FQDNs)
-
Environment assumptions (ports, paths, memory settings)
-
State management (what state exists, where it's stored)
Build a clean inventory of your service-oriented architecture so you can scope and sequence the transformation.
Step 2: Create Dockerfiles for Each Service
Encapsulate each service's runtime environment using a Dockerfile. Consistency matters.
For Spring Boot apps:
FROM eclipse-temurin:17-jdk
WORKDIR /app
COPY ./target/my-service.jar app.jar
CMD ["java", "-jar", "app.jar"]
For WAR/EAR apps, you'll need appropriate base images (such as Tomcat or WebLogic) and custom entrypoints.
Use official base images or vendor-supported options. Avoid unmaintained or custom community images.
Step 3: Externalize Configuration
Hardcoded values are a container anti-pattern. Externalize settings like DB credentials, API endpoints, and feature flags using environment variables or mounted config files.
Design your containers to read from their environment. This ensures portability and prepares them for ConfigMaps and Secrets in Kubernetes.
Step 4: Build and Test Locally
Test each container in isolation and with others using docker-compose
. Validate:
-
Clean startup and shutdown
-
Dependency connectivity
-
Statelessness and restart behavior
-
Logging to stdout/stderr
-
Runtime configuration injection
This gives you confidence before layering on orchestration.
Step 5: Handle State Properly
Containers are ephemeral. If your services write to disk, re-architect them to use mounted volumes or external storage.
Watch for legacy behaviors like sticky sessions or shared directories. Kubernetes will not tolerate them well.
Step 6: Create Kubernetes Manifests
For each service, create:
-
Deployment to define replicas and rolling updates
-
Service to enable internal discovery
-
Ingress to expose endpoints externally
-
ConfigMap or Secret to provide externalized settings
-
Probes for health and readiness
This is where you codify your deployment model. If you're using GitOps, this is your source of truth.
Step 7: Deploy to Kubernetes
Push images to a container registry. Deploy manifests into a Kubernetes cluster. Start small:
-
Deploy one or two services
-
Verify behavior, connectivity, and logs
-
Scale incrementally
Use kubectl
to monitor pods, logs, and deployments. Tools like Portainer can simplify visibility and governance.
Step 8: Configure Probes for Health Management
Enable Kubernetes to self-heal and manage availability.
-
Liveness probes restart crashed containers
-
Readiness probes gate traffic until services are ready
Use endpoints like /actuator/health
or custom health checks.
Step 9: Scale and Operate
With your services running in Kubernetes:
-
Use Horizontal Pod Autoscaler for demand-based scaling
-
Use rolling updates for zero-downtime deployments
-
Integrate CI/CD pipelines for continuous rollout
-
Use observability tools such as OpenTelemetry, Grafana, and Portainer for full visibility
You are no longer maintaining pets. You are managing cattle: scalable, reproducible, and observable units of compute.
Step 10: Update CI/CD and DevOps Practices
Migrating to containers and Kubernetes is also a cultural and workflow shift. Your CI/CD and DevOps processes need to evolve to support the new reality.
- Replace manual deployments with Git-based automation and GitOps workflows
- Break down large, brittle pipelines into modular build, test, scan, and deploy stages
- Use container-native CI pipelines to build, test, and scan images consistently
- Integrate security tools such as SAST, DAST, and container scanning into the pipeline
- Shift infrastructure configuration and deployment logic into source-controlled repositories.
Step 11: Revisit Git and Branching Strategy
Legacy version control patterns often don't translate well to modern, CI-driven workflows.
- Use short-lived feature branches with automated PR checks and approvals
- Establish clear naming conventions and branching policies
- Protect mainline branches with merge checks, approvals, and quality gates
- Encourage trunk-based development wherever feasible for fast feedback and reduced merge debt
A clean, scalable Git strategy is foundational to everything from automation to auditability.
Key Readiness Items to Keep in Mind
As part of our containerization readiness checklist, ensure:
-
Dockerfiles use supported base images and multi-stage builds
-
Configuration is externalized
-
Logging is standardized
-
CI/CD pipelines are integrated
-
Secrets are handled securely
-
Containers are stateless
-
Probes are defined and tuned
Need Help?
This is a bird’s eye view of the journey. We’ve helped organizations modernize ERP systems, Java SOA estates, and everything in between.
If you're looking to skip the trial and error and accelerate with confidence, the Portainer Managed Services team is here to help. We bring the tools, templates, and experience needed to modernize your apps and get them production-ready on Kubernetes.
Let’s talk.
COMMENTS