Article
DevSpace is a powerful developer tool for Kubernetes that enables fast, efficient local development by creating a smooth development loop directly in your cluster. For Java developers, DevSpace eliminates the traditional "build, push, deploy" cycle, providing instant code synchronization, hot reloading, and streamlined debugging in a Kubernetes-native environment.
In this guide, we'll explore how to supercharge your Java development workflow with DevSpace, from basic setup to advanced development scenarios.
Why DevSpace for Java Development?
- Instant Code Synchronization: See changes in seconds without rebuilding containers
- Hot Reloading: Automatic application restarts on code changes
- IDE-like Debugging: Direct debugging in Kubernetes pods
- Consistent Environments: Develop in production-like Kubernetes environments
- Team Standardization: Share development configurations across teams
- Multi-Service Development: Work with multiple microservices simultaneously
Part 1: DevSpace Architecture Overview
1.1 How DevSpace Works with Java
Local Java Code → DevSpace Sync → Kubernetes Pod → Running Application ↓ ↓ ↓ ↓ IDE Changes File Sync Hot Reload Instant Feedback Debugging Port Forward Log Streaming Breakpoints
1.2 Project Structure
java-devspace-app/ ├── src/ │ ├── main/java/com/example/ │ └── test/java/com/example/ ├── .devspace/ │ ├── devspace.yaml │ └── commands.yaml ├── charts/ │ └── app/ │ ├── Chart.yaml │ ├── values.yaml │ └── templates/ ├── Dockerfile ├── Dockerfile.dev ├── devspace-start.sh └── pom.xml
Part 2: Setup and Configuration
2.1 Installation
macOS:
# Homebrew brew install devspace # Or curl curl -L -o devspace "https://github.com/loft-sh/devspace/releases/latest/download/devspace-darwin-amd64" && sudo install -c -m 0755 devspace /usr/local/bin
Linux:
curl -L -o devspace "https://github.com/loft-sh/devspace/releases/latest/download/devspace-linux-amd64" && sudo install -c -m 0755 devspace /usr/local/bin
Windows:
# Chocolatey choco install devspace # Or PowerShell iwr -useb 'https://github.com/loft-sh/devspace/releases/latest/download/devspace-windows-amd64.exe' -o devspace.exe
2.2 Basic DevSpace Configuration
# .devspace/devspace.yaml
version: v2beta1
name: java-devspace-app
# Variables
vars:
IMAGE_NAME: java-app
REGISTRY: docker.io
REPOSITORY: myorg
JAVA_VERSION: 17
MAVEN_VERSION: 3.8.6
# Docker image building
images:
app:
image: ${REGISTRY}/${REPOSITORY}/${IMAGE_NAME}
tags:
- latest
- ${DEVSPACE_COMMIT}
build:
docker:
dockerfile: ./Dockerfile.dev
context: .
options:
buildArgs:
JAVA_VERSION: ${JAVA_VERSION}
MAVEN_VERSION: ${MAVEN_VERSION}
# Development configuration
dev:
app:
imageSelector: ${images.app.image}
devImage: ${images.app.image}
containers:
- container: app
restartHelper:
enabled: true
path: /restart
# Deployment configuration
deployments:
- name: ${name}
helm:
componentChart: true
values:
containers:
- image: ${images.app.image}
name: app
service:
ports:
- port: 8080
containerPort: 8080
# Development synchronization and port forwarding
profiles:
- name: dev
patches:
- op: add
path: dev.app.containers.0.sync
value:
localSubPath: ./src
containerPath: /app/src
excludePaths:
- "**/target/**"
- "**/.git/**"
- "**/node_modules/**"
- "**/*.class"
- op: add
path: dev.app.containers.0.portForwarding
value:
- port: 5005
remotePort: 5005
- port: 8080
remotePort: 8080
- op: add
path: dev.app.containers.0.logs
value:
enabled: true
lastLines: 100
- name: debug
patches:
- op: add
path: dev.app.containers.0.debug
value:
enabled: true
remoteEnabled: true
# Dependencies (for microservices)
dependencies:
- name: database
source:
git: https://github.com/myorg/devspace-database
subPath: charts/database
# Hooks for automation
hooks:
- command: ./scripts/pre-dev.sh
events:
before: deploy
- command: ./scripts/post-dev.sh
events:
after: deploy
Part 3: Docker Configuration for Development
3.1 Development Dockerfile
# Dockerfile.dev
FROM maven:${MAVEN_VERSION:-3.8.6}-openjdk-${JAVA_VERSION:-17} as dev
# Install additional development tools
RUN apt-get update && apt-get install -y \
curl \
git \
vim \
net-tools \
iputils-ping \
dnsutils \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user
RUN groupadd -r spring && useradd -r -g spring spring
# Set working directory
WORKDIR /app
# Copy Maven files for dependency caching
COPY pom.xml .
COPY .mvn/ .mvn/
COPY mvnw .
# Download dependencies
RUN mvn dependency:go-offline -B
# Copy source code
COPY src ./src
# Create restart trigger file
RUN mkdir -p /restart && touch /restart/.devrestart
# Development environment variables
ENV JAVA_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -Dspring.devtools.restart.enabled=true -Dspring.devtools.restart.trigger-file=/restart/.devrestart"
ENV MAVEN_OPTS="-Xmx1024m -XX:MaxPermSize=256m"
# Expose ports
EXPOSE 8080 5005
# Use non-root user
USER spring
# Development entry point
CMD ["mvn", "spring-boot:run", "-Dspring-boot.run.jvmArguments=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"]
3.2 Production Dockerfile
# Dockerfile # Build stage FROM maven:3.8.6-openjdk-17 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn clean package -DskipTests # Runtime stage FROM eclipse-temurin:17-jre-jammy RUN groupadd -r spring && useradd -r -g spring spring WORKDIR /app USER spring COPY --from=builder /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Part 4: Java Application Configuration
4.1 Spring Boot Development Configuration
// File: src/main/java/com/example/DevSpaceApplication.java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(DevSpaceProperties.class)
public class DevSpaceApplication {
public static void main(String[] args) {
SpringApplication.run(DevSpaceApplication.class, args);
}
}
// File: src/main/java/com/example/DevSpaceProperties.java
package com.example;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "app.devspace")
public record DevSpaceProperties(
boolean hotReload,
boolean remoteDebug,
int debugPort,
SyncConfig sync,
LoggingConfig logging
) {
public record SyncConfig(
boolean enabled,
String[] excludePatterns,
int pollInterval
) {}
public record LoggingConfig(
boolean devTools,
String level,
boolean showSql
) {}
}
4.2 Development-Specific Configuration
# src/main/resources/application-devspace.yaml app: devspace: hot-reload: true remote-debug: true debug-port: 5005 sync: enabled: true exclude-patterns: "**/target/**,**/test-output/**,**/.git/**" poll-interval: 1000 logging: dev-tools: true level: DEBUG show-sql: true spring: devtools: restart: enabled: true trigger-file: .devrestart poll-interval: 1000 quiet-period: 400 livereload: enabled: true jackson: serialization: indent-output: true logging: level: com.example: DEBUG org.springframework.web: DEBUG org.hibernate.SQL: DEBUG org.hibernate.type.descriptor.sql.BasicBinder: TRACE management: endpoints: web: exposure: include: health,info,metrics,restart endpoint: restart: enabled: true health: show-details: always server: port: 8080
4.3 Development Controller with Hot Reload Support
// File: src/main/java/com/example/controller/DevSpaceController.java
package com.example.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
import org.springframework.core.env.Environment;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.Map;
@RestController
@RequestMapping("/api/dev")
public class DevSpaceController {
private static final Logger logger = LoggerFactory.getLogger(DevSpaceController.class);
private final Environment environment;
public DevSpaceController(Environment environment) {
this.environment = environment;
}
@GetMapping("/info")
public ResponseEntity<Map<String, Object>> getDevInfo() {
logger.debug("Development info endpoint called");
return ResponseEntity.ok(Map.of(
"service", "java-devspace-app",
"timestamp", LocalDateTime.now(),
"profiles", environment.getActiveProfiles(),
"hotReload", environment.getProperty("app.devspace.hot-reload", Boolean.class, false),
"remoteDebug", environment.getProperty("app.devspace.remote-debug", Boolean.class, false),
"devTools", environment.getProperty("spring.devtools.restart.enabled", Boolean.class, false)
));
}
@PostMapping("/restart")
public ResponseEntity<Map<String, String>> triggerRestart() {
logger.info("Manual restart triggered via DevSpace endpoint");
// This will trigger Spring Boot DevTools restart
// In a real scenario, you might touch the trigger file
return ResponseEntity.ok(Map.of(
"status", "restart_triggered",
"message", "Application restart has been triggered"
));
}
@GetMapping("/config")
public ResponseEntity<Map<String, Object>> getConfig() {
return ResponseEntity.ok(Map.of(
"java.version", System.getProperty("java.version"),
"user.dir", System.getProperty("user.dir"),
"os.name", System.getProperty("os.name"),
"spring.profiles.active", String.join(",", environment.getActiveProfiles())
));
}
}
Part 5: Advanced DevSpace Configuration
5.1 Multi-Service Development
# .devspace/devspace-multi.yaml
version: v2beta1
name: java-microservices-dev
vars:
REGISTRY: docker.io
REPOSITORY: myorg
images:
user-service:
image: ${REGISTRY}/${REPOSITORY}/user-service
tags: ["latest"]
build:
docker:
dockerfile: ./user-service/Dockerfile.dev
context: ./user-service
order-service:
image: ${REGISTRY}/${REPOSITORY}/order-service
tags: ["latest"]
build:
docker:
dockerfile: ./order-service/Dockerfile.dev
context: ./order-service
api-gateway:
image: ${REGISTRY}/${REPOSITORY}/api-gateway
tags: ["latest"]
build:
docker:
dockerfile: ./api-gateway/Dockerfile.dev
context: ./api-gateway
dev:
user-service:
imageSelector: ${images.user-service.image}
containers:
- container: app
sync:
- localSubPath: ./user-service/src
containerPath: /app/src
excludePaths:
- "**/target/**"
portForwarding:
- port: 8081
remotePort: 8081
- port: 5006
remotePort: 5006
order-service:
imageSelector: ${images.order-service.image}
containers:
- container: app
sync:
- localSubPath: ./order-service/src
containerPath: /app/src
portForwarding:
- port: 8082
remotePort: 8082
- port: 5007
remotePort: 5007
api-gateway:
imageSelector: ${images.api-gateway.image}
containers:
- container: app
sync:
- localSubPath: ./api-gateway/src
containerPath: /app/src
portForwarding:
- port: 8080
remotePort: 8080
- port: 5005
remotePort: 5005
deployments:
- name: user-service
helm:
componentChart: true
values:
containers:
- image: ${images.user-service.image}
service:
ports:
- port: 8081
- name: order-service
helm:
componentChart: true
values:
containers:
- image: ${images.order-service.image}
service:
ports:
- port: 8082
- name: api-gateway
helm:
componentChart: true
values:
containers:
- image: ${images.api-gateway.image}
service:
ports:
- port: 8080
profiles:
- name: with-dependencies
patches:
- op: add
path: dependencies
value:
- name: postgres
source:
git: https://github.com/loft-sh/component-charts
subPath: postgresql
version: 0.1.0
- name: redis
source:
git: https://github.com/loft-sh/component-charts
subPath: redis
version: 0.1.0
5.2 Custom Commands and Scripts
# .devspace/commands.yaml version: v1beta1 commands: - name: dev description: Start development mode with hot reload command: |- devspace dev --profile dev - name: debug description: Start development with remote debugging command: |- devspace dev --profile debug - name: build description: Build application image command: |- devspace build - name: deploy description: Deploy to development namespace command: |- devspace deploy --profile dev - name: logs description: Show application logs command: |- devspace logs -f - name: test description: Run tests in development environment command: |- devspace enter --container app -- mvn test - name: integration-test description: Run integration tests command: |- devspace run-pipeline integration-tests - name: clean description: Clean development environment command: |- devspace purge kubectl delete pod -l app.kubernetes.io/name=java-devspace-app - name: restart description: Restart development container command: |- devspace restart - name: status description: Show development status command: |- echo "=== Development Environment Status ===" kubectl get pods -l app.kubernetes.io/name=java-devspace-app echo "" echo "=== Port Forwarding ===" kubectl get svc -l app.kubernetes.io/name=java-devspace-app
5.3 Pipeline Configuration
# .devspace/pipelines.yaml version: v1beta1 pipelines: dev: run: |- devspace build --skip-push devspace deploy devspace dev integration-tests: run: |- devspace build --skip-push devspace deploy devspace enter --container app -- mvn verify debug: run: |- devspace build --skip-push devspace deploy devspace dev --debug reset: run: |- devspace purge kubectl delete pvc -l app.kubernetes.io/name=java-devspace-app
Part 6: Development Workflows
6.1 Startup Script
#!/bin/bash # devspace-start.sh set -e echo "🚀 Starting Java DevSpace Development Environment" # Check if DevSpace is installed if ! command -v devspace &> /dev/null; then echo "❌ DevSpace is not installed. Please install it first." echo " Visit: https://devspace.sh/cli/docs/getting-started/installation" exit 1 fi # Check if we have a Kubernetes context if ! kubectl config current-context &> /dev/null; then echo "❌ No Kubernetes context found. Please configure kubectl." exit 1 fi echo "✅ Kubernetes context: $(kubectl config current-context)" # Build and start development environment echo "📦 Building application image..." devspace build echo "🚀 Deploying to Kubernetes..." devspace deploy echo "🔧 Starting development mode with hot reload..." devspace dev --profile dev echo "🎉 Development environment is ready!" echo " Application: http://localhost:8080" echo " Actuator: http://localhost:8080/actuator" echo " Dev Info: http://localhost:8080/api/dev/info"
6.2 Development Makefile
# Makefile .DEFAULT_GOAL := help .PHONY: help dev debug build deploy test clean logs restart status help: @echo "Java DevSpace Development Commands:" @echo " make dev - Start development mode with hot reload" @echo " make debug - Start development with remote debugging" @echo " make build - Build application image" @echo " make deploy - Deploy to development namespace" @echo " make test - Run tests in development environment" @echo " make logs - Show application logs" @echo " make restart - Restart development container" @echo " make status - Show development status" @echo " make clean - Clean development environment" dev: ./devspace-start.sh debug: devspace dev --profile debug build: devspace build deploy: devspace deploy --profile dev test: devspace enter --container app -- mvn test integration-test: devspace run-pipeline integration-tests logs: devspace logs -f --tail 100 restart: devspace restart status: @echo "=== Development Environment Status ===" @kubectl get pods -l app.kubernetes.io/name=java-devspace-app @echo "" @echo "=== Services ===" @kubectl get svc -l app.kubernetes.io/name=java-devspace-app clean: devspace purge kubectl delete pod -l app.kubernetes.io/name=java-devspace-app purge-all: devspace purge kubectl delete all -l app.kubernetes.io/name=java-devspace-app kubectl delete pvc -l app.kubernetes.io/name=java-devspace-app
Part 7: IDE Integration
7.1 VS Code Configuration
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "DevSpace Remote Debug",
"request": "attach",
"hostName": "localhost",
"port": 5005,
"projectName": "java-devspace-app"
},
{
"type": "java",
"name": "DevSpace Test Debug",
"request": "attach",
"hostName": "localhost",
"port": 5006,
"projectName": "java-devspace-app"
}
]
}
// .vscode/settings.json
{
"java.configuration.updateBuildConfiguration": "automatic",
"java.compile.nullAnalysis.mode": "automatic",
"java.debug.settings.onBuildFailureProceed": true,
"files.watcherExclude": {
"**/target/**": true,
"**/build/**": true
},
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable"
}
7.2 IntelliJ IDEA Configuration
<!-- .idea/runConfigurations/DevSpace_Remote_Debug.xml --> <component name="ProjectRunConfigurationManager"> <configuration default="false" name="DevSpace Remote Debug" type="Remote"> <module name="java-devspace-app" /> <option name="USE_SOCKET_TRANSPORT" value="true" /> <option name="SERVER_MODE" value="false" /> <option name="SHMEM_ADDRESS" /> <option name="HOST" value="localhost" /> <option name="PORT" value="5005" /> <option name="AUTO_RESTART" value="false" /> <RunnerSettings RunnerId="Debug" /> <ConfigurationWrapper RunnerId="Debug" /> <method v="2" /> </configuration> </component>
Part 8: Team Development Setup
8.1 Development Environment Bootstrap
# .devspace/team-setup.yaml
version: v2beta1
name: java-team-dev
vars:
NAMESPACE: java-dev-${DEVSPACE_USERNAME}
REGISTRY: docker.io
REPOSITORY: myorg
profiles:
- name: team-dev
patches:
- op: replace
path: deployments.helm.values.labels
value:
app.kubernetes.io/name: ${name}
app.kubernetes.io/instance: ${DEVSPACE_USERNAME}
- op: replace
path: deployments.helm.values.namespace
value: ${NAMESPACE}
- op: add
path: deployments.helm.values.ingress
value:
enabled: true
hosts:
- host: ${DEVSPACE_USERNAME}.dev.mycompany.com
paths:
- path: /
pathType: Prefix
commands:
- name: team-init
description: Initialize team development environment
command: |-
# Create namespace for user
kubectl create namespace ${NAMESPACE} || true
# Set up development context
kubectl config set-context --current --namespace=${NAMESPACE}
# Start development
devspace dev --profile team-dev
- name: team-cleanup
description: Clean up team development environment
command: |-
kubectl delete namespace ${NAMESPACE} || true
8.2 Development Dependencies
# .devspace/dependencies.yaml version: v2beta1 dependencies: - name: postgresql source: git: https://github.com/bitnami/charts subPath: bitnami/postgresql version: 12.0.0 values: |- auth: postgresPassword: "devspace123" database: "java_app" primary: persistence: enabled: false - name: redis source: git: https://github.com/bitnami/charts subPath: bitnami/redis version: 17.0.0 values: |- architecture: standalone auth: enabled: false master: persistence: enabled: false - name: elasticsearch source: git: https://github.com/elastic/helm-charts subPath: elasticsearch version: 7.17.0 values: |- replicas: 1 minimumMasterNodes: 1 resources: requests: cpu: 100m memory: 512Mi limits: cpu: 1000m memory: 1Gi
Part 9: Troubleshooting and Optimization
9.1 Common Issues and Solutions
#!/bin/bash
# scripts/troubleshoot-devspace.sh
echo "🔧 DevSpace Troubleshooting Script"
echo "1. Checking Kubernetes connection..."
kubectl cluster-info
echo "2. Checking current namespace..."
kubectl config view --minify --output 'jsonpath={..namespace}'; echo
echo "3. Checking DevSpace pods..."
kubectl get pods -l app.kubernetes.io/name=java-devspace-app
echo "4. Checking sync status..."
devspace list sync
echo "5. Checking port forwarding..."
devspace list ports
echo "6. Checking logs..."
devspace logs --tail 50
echo "7. Restarting sync..."
devspace sync --restart
echo "✅ Troubleshooting complete"
9.2 Performance Optimization
# .devspace/optimized.yaml
version: v2beta1
name: java-devspace-optimized
profiles:
- name: optimized
patches:
- op: replace
path: images.app.build.docker.options
value:
buildArgs:
JAVA_VERSION: "17"
MAVEN_VERSION: "3.8.6"
target: dev
cacheFrom:
- ${images.app.image}:latest
- op: replace
path: dev.app.containers.0.sync
value:
localSubPath: ./src
containerPath: /app/src
excludePaths:
- "**/target/**"
- "**/test-output/**"
- "**/.git/**"
- "**/node_modules/**"
- "**/*.class"
- "**/*.jar"
- "**/logs/**"
uploadExcludeFile: .dockerignore
poll: false
bandwidthLimits: false
- op: add
path: dev.app.containers.0.resources
value:
limits:
memory: 1Gi
cpu: 500m
requests:
memory: 512Mi
cpu: 100m
Best Practices for DevSpace with Java
- Use Development-Specific Dockerfiles: Optimize for fast builds and hot reloading
- Leverage Sync Exclusions: Exclude build artifacts and dependencies from sync
- Configure Resource Limits: Prevent development containers from consuming too many resources
- Use Profiles: Create different profiles for debug, test, and team development
- Implement Health Checks: Ensure your application is ready before starting sync
- Version Control Configurations: Share DevSpace configurations across the team
- Monitor Performance: Use DevSpace analytics to optimize your development loop
- Secure Development: Use namespaced development environments for security
Conclusion
DevSpace revolutionizes Java development on Kubernetes by providing a seamless, fast, and efficient development experience. By implementing the patterns and configurations in this guide, you can:
- Eliminate long build cycles with instant code synchronization
- Debug applications directly in Kubernetes with full IDE integration
- Maintain consistent environments across your development team
- Streamline microservices development with multi-service support
- Automate development workflows with custom commands and pipelines
The combination of DevSpace's powerful synchronization, Java's robust ecosystem, and Kubernetes' scalability creates an unparalleled development experience that accelerates feature development while maintaining production-like environment fidelity.