Google Cloud Run with Java: Building Serverless Containers

Google Cloud Run is a fully managed serverless platform that automatically scales your stateless containers. This guide covers building, deploying, and optimizing Java applications for Cloud Run.


Core Concepts

Why Cloud Run for Java?

  • Serverless: No infrastructure management
  • Auto-scaling: Scales to zero when not in use
  • Pay-per-use: Only pay for request processing time
  • Container-based: Use any Java framework or library
  • Fast deployment: Quick iterations and deployments

Key Considerations for Java on Cloud Run:

  • Cold Start Optimization: Minimize startup time
  • Memory Management: Configure appropriate memory limits
  • Stateless Design: No local storage or session state
  • Health Checks: Implement proper readiness/liveness probes
  • Concurrency: Handle multiple requests per container

Dependencies and Setup

1. Maven Dependencies
<properties>
<spring-boot.version>3.1.0</spring-boot.version>
<jib-maven-plugin.version>3.4.0</jib-maven-plugin.version>
<google-cloud.version>2.20.0</google-cloud.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Spring Boot Actuator for Health Checks -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Spring Boot Configuration Processor -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
<optional>true</optional>
</dependency>
<!-- Google Cloud Libraries -->
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-storage</artifactId>
<version>${google-cloud.version}</version>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-secretmanager</artifactId>
<version>${google-cloud.version}</version>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-logging</artifactId>
<version>${google-cloud.version}</version>
</dependency>
<!-- Database (PostgreSQL) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
</dependency>
<!-- Redis for Caching -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<!-- Testcontainers for Integration Tests -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.18.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.18.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Spring Boot Maven Plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<!-- Jib for Container Building -->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>${jib-maven-plugin.version}</version>
<configuration>
<from>
<image>eclipse-temurin:21-jre</image>
</from>
<to>
<image>gcr.io/${google.cloud.project}/${project.artifactId}:${project.version}</image>
</to>
<container>
<jvmFlags>
<jvmFlag>-Xmx512m</jvmFlag>
<jvmFlag>-Xms256m</jvmFlag>
<jvmFlag>-XX:+UseG1GC</jvmFlag>
<jvmFlag>-XX:MaxRAMPercentage=75.0</jvmFlag>
</jvmFlags>
<environment>
<SPRING_PROFILES_ACTIVE>cloudrun</SPRING_PROFILES_ACTIVE>
</environment>
<ports>
<port>8080</port>
</ports>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
</container>
</configuration>
</plugin>
</plugins>
</build>
2. Google Cloud SDK Setup
# Install Google Cloud SDK
curl https://sdk.cloud.google.com | bash
exec -l $SHELL
# Initialize and authenticate
gcloud init
gcloud auth configure-docker
# Set project
gcloud config set project YOUR_PROJECT_ID

Application Implementation

1. Main Application Class
package com.example.cloudrun;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(CloudRunProperties.class)
public class CloudRunApplication {
public static void main(String[] args) {
SpringApplication.run(CloudRunApplication.class, args);
}
}
2. Cloud Run Configuration
package com.example.cloudrun.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "cloudrun")
public class CloudRunProperties {
private String serviceName;
private String revision;
private String configuration;
private int maxInstances = 100;
private int concurrency = 80;
private int timeoutSeconds = 300;
private String region = "us-central1";
// Getters and Setters
public String getServiceName() { return serviceName; }
public void setServiceName(String serviceName) { this.serviceName = serviceName; }
public String getRevision() { return revision; }
public void setRevision(String revision) { this.revision = revision; }
public String getConfiguration() { return configuration; }
public void setConfiguration(String configuration) { this.configuration = configuration; }
public int getMaxInstances() { return maxInstances; }
public void setMaxInstances(int maxInstances) { this.maxInstances = maxInstances; }
public int getConcurrency() { return concurrency; }
public void setConcurrency(int concurrency) { this.concurrency = concurrency; }
public int getTimeoutSeconds() { return timeoutSeconds; }
public void setTimeoutSeconds(int timeoutSeconds) { this.timeoutSeconds = timeoutSeconds; }
public String getRegion() { return region; }
public void setRegion(String region) { this.region = region; }
}
3. Application Configuration
# application-cloudrun.yml
server:
port: 8080
servlet:
context-path: /
compression:
enabled: true
mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
min-response-size: 1024
spring:
application:
name: cloudrun-demo
datasource:
url: ${DATABASE_URL:jdbc:postgresql://localhost:5432/cloudrun}
username: ${DATABASE_USERNAME:postgres}
password: ${DATABASE_PASSWORD:password}
hikari:
maximum-pool-size: 5
minimum-idle: 1
connection-timeout: 30000
max-lifetime: 1800000
jpa:
hibernate:
ddl-auto: validate
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
jdbc:
lob:
non_contextual_creation: true
show_sql: false
format_sql: false
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 1
max-wait: -1ms
cloud:
gcp:
project-id: ${GOOGLE_CLOUD_PROJECT}
credentials:
location: ${GOOGLE_APPLICATION_CREDENTIALS:}
management:
endpoints:
web:
exposure:
include: health,info,metrics,env
base-path: /actuator
endpoint:
health:
show-details: always
show-components: always
probes:
enabled: true
metrics:
enabled: true
cloudrun:
service-name: ${K_SERVICE:local}
revision: ${K_REVISION:local}
configuration: ${K_CONFIGURATION:local}
max-instances: ${MAX_INSTANCES:100}
concurrency: ${CONCURRENCY:80}
timeout-seconds: ${TIMEOUT_SECONDS:300}
region: ${REGION:us-central1}
logging:
level:
com.example.cloudrun: INFO
org.hibernate.SQL: WARN
org.springframework.cloud.gcp: INFO
pattern:
level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
# Optimize for Cloud Run
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
threads:
virtual:
enabled: true
4. Health Check Configuration
package com.example.cloudrun.health;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Map;
@Component
public class CloudRunHealthIndicator implements HealthIndicator {
private final DataSource dataSource;
private final CloudRunMetadata cloudRunMetadata;
public CloudRunHealthIndicator(DataSource dataSource, CloudRunMetadata cloudRunMetadata) {
this.dataSource = dataSource;
this.cloudRunMetadata = cloudRunMetadata;
}
@Override
public Health health() {
try {
// Check database connectivity
boolean dbHealthy = checkDatabaseHealth();
// Build health details
Health.Builder healthBuilder = dbHealthy ? 
Health.up() : Health.down();
healthBuilder
.withDetail("service", cloudRunMetadata.getServiceName())
.withDetail("revision", cloudRunMetadata.getRevision())
.withDetail("region", cloudRunMetadata.getRegion())
.withDetail("timestamp", System.currentTimeMillis())
.withDetails(buildHealthDetails(dbHealthy));
return healthBuilder.build();
} catch (Exception e) {
return Health.down(e)
.withDetail("service", cloudRunMetadata.getServiceName())
.withDetail("error", e.getMessage())
.build();
}
}
private boolean checkDatabaseHealth() {
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
return statement.execute("SELECT 1");
} catch (Exception e) {
return false;
}
}
private Map<String, Object> buildHealthDetails(boolean dbHealthy) {
return Map.of(
"database", dbHealthy ? "CONNECTED" : "DISCONNECTED",
"memory_usage", Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(),
"available_processors", Runtime.getRuntime().availableProcessors(),
"uptime", ManagementFactory.getRuntimeMXBean().getUptime()
);
}
}
@Component
class CloudRunMetadata {
@Value("${K_SERVICE:local}")
private String serviceName;
@Value("${K_REVISION:local}")
private String revision;
@Value("${REGION:us-central1}")
private String region;
public String getServiceName() { return serviceName; }
public String getRevision() { return revision; }
public String getRegion() { return region; }
}
5. Domain Models
package com.example.cloudrun.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.UUID;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
@NotBlank
@Size(max = 100)
@Column(nullable = false)
private String firstName;
@NotBlank
@Size(max = 100)
@Column(nullable = false)
private String lastName;
@NotBlank
@Email
@Size(max = 255)
@Column(nullable = false, unique = true)
private String email;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private UserStatus status = UserStatus.ACTIVE;
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at", nullable = false)
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
// Constructors
public User() {}
public User(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
// Getters and Setters
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public UserStatus getStatus() { return status; }
public void setStatus(UserStatus status) { this.status = status; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}
enum UserStatus {
ACTIVE, INACTIVE, SUSPENDED
}
// Data Transfer Objects
public class UserResponse {
private final UUID id;
private final String firstName;
private final String lastName;
private final String email;
private final UserStatus status;
private final LocalDateTime createdAt;
public UserResponse(User user) {
this.id = user.getId();
this.firstName = user.getFirstName();
this.lastName = user.getLastName();
this.email = user.getEmail();
this.status = user.getStatus();
this.createdAt = user.getCreatedAt();
}
// Getters
public UUID getId() { return id; }
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public String getEmail() { return email; }
public UserStatus getStatus() { return status; }
public LocalDateTime getCreatedAt() { return createdAt; }
}
public class CreateUserRequest {
@NotBlank
@Size(max = 100)
private String firstName;
@NotBlank
@Size(max = 100)
private String lastName;
@NotBlank
@Email
@Size(max = 255)
private String email;
// Getters and Setters
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
6. Repository Layer
package com.example.cloudrun.repository;
import com.example.cloudrun.model.User;
import com.example.cloudrun.model.UserStatus;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Repository
public interface UserRepository extends JpaRepository<User, UUID> {
Optional<User> findByEmail(String email);
List<User> findByStatus(UserStatus status);
Page<User> findByStatus(UserStatus status, Pageable pageable);
@Query("SELECT u FROM User u WHERE LOWER(u.firstName) LIKE LOWER(CONCAT('%', :name, '%')) OR LOWER(u.lastName) LIKE LOWER(CONCAT('%', :name, '%'))")
Page<User> findByNameContainingIgnoreCase(@Param("name") String name, Pageable pageable);
boolean existsByEmail(String email);
long countByStatus(UserStatus status);
}
7. Service Layer
package com.example.cloudrun.service;
import com.example.cloudrun.model.User;
import com.example.cloudrun.model.UserStatus;
import com.example.cloudrun.repository.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
import java.util.UUID;
@Service
@Transactional
public class UserService {
private static final Logger LOG = LoggerFactory.getLogger(UserService.class);
private final UserRepository userRepository;
private final CloudRunMetrics metrics;
public UserService(UserRepository userRepository, CloudRunMetrics metrics) {
this.userRepository = userRepository;
this.metrics = metrics;
}
@Transactional(readOnly = true)
public Page<User> getAllUsers(Pageable pageable) {
LOG.debug("Fetching users page: {}", pageable);
metrics.recordUserQuery();
return userRepository.findAll(pageable);
}
@Transactional(readOnly = true)
@Cacheable(value = "users", key = "#id")
public Optional<User> getUserById(UUID id) {
LOG.debug("Fetching user by ID: {}", id);
metrics.recordUserQuery();
return userRepository.findById(id);
}
@Transactional(readOnly = true)
public Optional<User> getUserByEmail(String email) {
LOG.debug("Fetching user by email: {}", email);
metrics.recordUserQuery();
return userRepository.findByEmail(email);
}
public User createUser(User user) {
LOG.info("Creating new user with email: {}", user.getEmail());
if (userRepository.existsByEmail(user.getEmail())) {
throw new UserServiceException("User with email " + user.getEmail() + " already exists");
}
User savedUser = userRepository.save(user);
metrics.recordUserCreated();
LOG.info("Successfully created user: {}", savedUser.getId());
return savedUser;
}
public User updateUser(UUID id, User userUpdate) {
LOG.info("Updating user: {}", id);
User existingUser = userRepository.findById(id)
.orElseThrow(() -> new UserServiceException("User not found: " + id));
// Update fields if provided
if (userUpdate.getFirstName() != null) {
existingUser.setFirstName(userUpdate.getFirstName());
}
if (userUpdate.getLastName() != null) {
existingUser.setLastName(userUpdate.getLastName());
}
if (userUpdate.getStatus() != null) {
existingUser.setStatus(userUpdate.getStatus());
}
User updatedUser = userRepository.save(existingUser);
metrics.recordUserUpdated();
LOG.info("Successfully updated user: {}", id);
return updatedUser;
}
public void deleteUser(UUID id) {
LOG.info("Deleting user: {}", id);
User user = userRepository.findById(id)
.orElseThrow(() -> new UserServiceException("User not found: " + id));
userRepository.delete(user);
metrics.recordUserDeleted();
LOG.info("Successfully deleted user: {}", id);
}
@Transactional(readOnly = true)
public long getActiveUsersCount() {
long count = userRepository.countByStatus(UserStatus.ACTIVE);
metrics.recordUserQuery();
return count;
}
public User deactivateUser(UUID id) {
LOG.info("Deactivating user: {}", id);
User user = userRepository.findById(id)
.orElseThrow(() -> new UserServiceException("User not found: " + id));
user.setStatus(UserStatus.INACTIVE);
User deactivatedUser = userRepository.save(user);
metrics.recordUserDeactivated();
return deactivatedUser;
}
}
class UserServiceException extends RuntimeException {
public UserServiceException(String message) {
super(message);
}
public UserServiceException(String message, Throwable cause) {
super(message, cause);
}
}
8. REST Controllers
package com.example.cloudrun.controller;
import com.example.cloudrun.model.CreateUserRequest;
import com.example.cloudrun.model.User;
import com.example.cloudrun.model.UserResponse;
import com.example.cloudrun.service.UserService;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.UUID;
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
private static final Logger LOG = LoggerFactory.getLogger(UserController.class);
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public ResponseEntity<Page<UserResponse>> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(defaultValue = "createdAt") String sortBy,
@RequestParam(defaultValue = "desc") String sortDirection) {
LOG.info("Fetching users - page: {}, size: {}, sort: {}", page, size, sortBy);
Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), sortBy);
Pageable pageable = PageRequest.of(page, size, sort);
Page<UserResponse> users = userService.getAllUsers(pageable)
.map(UserResponse::new);
return ResponseEntity.ok(users);
}
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUserById(@PathVariable UUID id) {
LOG.info("Fetching user by ID: {}", id);
return userService.getUserById(id)
.map(user -> ResponseEntity.ok(new UserResponse(user)))
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<UserResponse> createUser(@Valid @RequestBody CreateUserRequest request) {
LOG.info("Creating user with email: {}", request.getEmail());
User user = new User(request.getFirstName(), request.getLastName(), request.getEmail());
User createdUser = userService.createUser(user);
return ResponseEntity.status(HttpStatus.CREATED)
.body(new UserResponse(createdUser));
}
@PutMapping("/{id}")
public ResponseEntity<UserResponse> updateUser(
@PathVariable UUID id,
@Valid @RequestBody CreateUserRequest request) {
LOG.info("Updating user: {}", id);
User userUpdate = new User(request.getFirstName(), request.getLastName(), request.getEmail());
User updatedUser = userService.updateUser(id, userUpdate);
return ResponseEntity.ok(new UserResponse(updatedUser));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable UUID id) {
LOG.info("Deleting user: {}", id);
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/{id}/deactivate")
public ResponseEntity<UserResponse> deactivateUser(@PathVariable UUID id) {
LOG.info("Deactivating user: {}", id);
User deactivatedUser = userService.deactivateUser(id);
return ResponseEntity.ok(new UserResponse(deactivatedUser));
}
@GetMapping("/stats/active-count")
public ResponseEntity<ActiveUsersCountResponse> getActiveUsersCount() {
long count = userService.getActiveUsersCount();
return ResponseEntity.ok(new ActiveUsersCountResponse(count));
}
}
class ActiveUsersCountResponse {
private final long activeUsersCount;
public ActiveUsersCountResponse(long activeUsersCount) {
this.activeUsersCount = activeUsersCount;
}
public long getActiveUsersCount() {
return activeUsersCount;
}
}
9. Google Cloud Integration Services
package com.example.cloudrun.integration;
import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.Storage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.util.UUID;
@Service
public class CloudStorageService {
private static final Logger LOG = LoggerFactory.getLogger(CloudStorageService.class);
private final Storage storage;
@Value("${gcp.storage.bucket:default-bucket}")
private String bucketName;
public CloudStorageService(Storage storage) {
this.storage = storage;
}
public String uploadFile(InputStream fileStream, String contentType, String originalFilename) {
try {
String fileName = generateFileName(originalFilename);
BlobId blobId = BlobId.of(bucketName, fileName);
BlobInfo blobInfo = BlobInfo.newBuilder(blobId)
.setContentType(contentType)
.build();
storage.createFrom(blobInfo, fileStream);
String publicUrl = String.format("https://storage.googleapis.com/%s/%s", bucketName, fileName);
LOG.info("File uploaded successfully: {}", publicUrl);
return publicUrl;
} catch (Exception e) {
LOG.error("Failed to upload file to Cloud Storage: {}", e.getMessage(), e);
throw new CloudStorageException("File upload failed", e);
}
}
public void deleteFile(String fileName) {
try {
boolean deleted = storage.delete(bucketName, fileName);
if (deleted) {
LOG.info("File deleted successfully: {}", fileName);
} else {
LOG.warn("File not found for deletion: {}", fileName);
}
} catch (Exception e) {
LOG.error("Failed to delete file from Cloud Storage: {}", e.getMessage(), e);
throw new CloudStorageException("File deletion failed", e);
}
}
private String generateFileName(String originalFilename) {
String extension = "";
if (originalFilename != null && originalFilename.contains(".")) {
extension = originalFilename.substring(originalFilename.lastIndexOf("."));
}
return "uploads/" + UUID.randomUUID() + extension;
}
}
class CloudStorageException extends RuntimeException {
public CloudStorageException(String message) {
super(message);
}
public CloudStorageException(String message, Throwable cause) {
super(message, cause);
}
}
package com.example.cloudrun.integration;
import com.google.cloud.secretmanager.v1.AccessSecretVersionResponse;
import com.google.cloud.secretmanager.v1.SecretManagerServiceClient;
import com.google.cloud.secretmanager.v1.SecretVersionName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.io.IOException;
@Service
public class SecretManagerService {
private static final Logger LOG = LoggerFactory.getLogger(SecretManagerService.class);
public String getSecret(String secretId, String version) {
try (SecretManagerServiceClient client = SecretManagerServiceClient.create()) {
String projectId = System.getenv("GOOGLE_CLOUD_PROJECT");
SecretVersionName secretVersionName = SecretVersionName.of(projectId, secretId, version);
AccessSecretVersionResponse response = client.accessSecretVersion(secretVersionName);
String secretValue = response.getPayload().getData().toStringUtf8();
LOG.debug("Successfully retrieved secret: {}", secretId);
return secretValue;
} catch (IOException e) {
LOG.error("Failed to retrieve secret {}: {}", secretId, e.getMessage());
throw new SecretManagerException("Failed to retrieve secret: " + secretId, e);
}
}
public String getSecret(String secretId) {
return getSecret(secretId, "latest");
}
}
class SecretManagerException extends RuntimeException {
public SecretManagerException(String message) {
super(message);
}
public SecretManagerException(String message, Throwable cause) {
super(message, cause);
}
}
10. Metrics and Monitoring
package com.example.cloudrun.metrics;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class CloudRunMetrics {
private final Counter requestsCounter;
private final Counter userCreatedCounter;
private final Counter userUpdatedCounter;
private final Counter userDeletedCounter;
private final Counter userDeactivatedCounter;
private final Counter userQueryCounter;
private final Timer requestTimer;
public CloudRunMetrics(MeterRegistry meterRegistry) {
this.requestsCounter = Counter.builder("cloudrun.requests.total")
.description("Total number of HTTP requests")
.register(meterRegistry);
this.userCreatedCounter = Counter.builder("cloudrun.users.created")
.description("Number of users created")
.register(meterRegistry);
this.userUpdatedCounter = Counter.builder("cloudrun.users.updated")
.description("Number of users updated")
.register(meterRegistry);
this.userDeletedCounter = Counter.builder("cloudrun.users.deleted")
.description("Number of users deleted")
.register(meterRegistry);
this.userDeactivatedCounter = Counter.builder("cloudrun.users.deactivated")
.description("Number of users deactivated")
.register(meterRegistry);
this.userQueryCounter = Counter.builder("cloudrun.users.queries")
.description("Number of user queries")
.register(meterRegistry);
this.requestTimer = Timer.builder("cloudrun.request.duration")
.description("HTTP request duration")
.register(meterRegistry);
}
public void recordRequest() {
requestsCounter.increment();
}
public void recordUserCreated() {
userCreatedCounter.increment();
}
public void recordUserUpdated() {
userUpdatedCounter.increment();
}
public void recordUserDeleted() {
userDeletedCounter.increment();
}
public void recordUserDeactivated() {
userDeactivatedCounter.increment();
}
public void recordUserQuery() {
userQueryCounter.increment();
}
public void recordRequestTime(long duration, TimeUnit unit) {
requestTimer.record(duration, unit);
}
}

Docker Configuration

1. Dockerfile for Cloud Run
# Use Eclipse Temurin JRE for smaller image size
FROM eclipse-temurin:21-jre-jammy as runtime
# Install curl for health checks
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/* && \
apt-get clean
# Create a non-root user
RUN groupadd --system --gid 1000 appgroup && \
useradd --system --uid 1000 --gid appgroup appuser
# Set working directory
WORKDIR /app
# Copy the JAR file
COPY target/*.jar app.jar
# Change ownership to non-root user
RUN chown -R appuser:appgroup /app
# Switch to non-root user
USER appuser
# Expose the port Cloud Run expects
EXPOSE 8080
# Health check (Cloud Run uses startup and liveness probes)
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
# Optimized JVM flags for Cloud Run
ENV JAVA_OPTS="-Xmx512m -Xms256m -XX:+UseG1GC -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom"
# Start the application
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"]
2. Jib Configuration for Maven
<!-- Enhanced Jib Configuration -->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<from>
<image>eclipse-temurin:21-jre-jammy</image>
<platforms>
<platform>
<architecture>amd64</architecture>
<os>linux</os>
</platform>
</platforms>
</from>
<to>
<image>gcr.io/${google.cloud.project}/${project.artifactId}</image>
<tags>
<tag>${project.version}</tag>
<tag>latest</tag>
</tags>
</to>
<container>
<user>1000:1000</user>
<workingDirectory>/app</workingDirectory>
<entrypoint>
<arg>java</arg>
<arg>-Xmx512m</arg>
<arg>-Xms256m</arg>
<arg>-XX:+UseG1GC</arg>
<arg>-XX:MaxRAMPercentage=75.0</arg>
<arg>-Djava.security.egd=file:/dev/./urandom</arg>
<arg>-jar</arg>
<arg>/app/app.jar</arg>
</entrypoint>
<ports>
<port>8080</port>
</ports>
<environment>
<SPRING_PROFILES_ACTIVE>cloudrun</SPRING_PROFILES_ACTIVE>
<JAVA_TOOL_OPTIONS>-XX:+ExitOnOutOfMemoryError</JAVA_TOOL_OPTIONS>
</environment>
<creationTime>USE_CURRENT_TIMESTAMP</creationTime>
<labels>
<maintainer>[email protected]</maintainer>
<version>${project.version}</version>
<commit-sha>${git.commit.id.abbrev}</commit-sha>
</labels>
</container>
</configuration>
</plugin>

Deployment Configuration

1. Cloud Run Service Definition
# service.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: cloudrun-demo
labels:
app: cloudrun-demo
environment: production
spec:
template:
metadata:
annotations:
# Auto-scaling configuration
autoscaling.knative.dev/maxScale: "100"
autoscaling.knative.dev/minScale: "0"
# Concurrency configuration
run.googleapis.com/concurrency: "80"
# CPU configuration
run.googleapis.com/cpu-throttling: "true"
# Startup configuration
run.googleapis.com/startup-cpu-boost: "true"
spec:
containerConcurrency: 80
timeoutSeconds: 300
containers:
- image: gcr.io/YOUR_PROJECT_ID/cloudrun-demo:latest
ports:
- containerPort: 8080
resources:
limits:
cpu: 1000m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
env:
- name: SPRING_PROFILES_ACTIVE
value: "cloudrun,production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: cloudrun-db-secret
key: url
- name: DATABASE_USERNAME
valueFrom:
secretKeyRef:
name: cloudrun-db-secret
key: username
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: cloudrun-db-secret
key: password
- name: REDIS_HOST
value: "10.0.0.3"
- name: MAX_INSTANCES
value: "100"
- name: CONCURRENCY
value: "80"
- name: TIMEOUT_SECONDS
value: "300"
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 0
periodSeconds: 5
timeoutSeconds: 10
failureThreshold: 30  # 30 * 5 = 150 seconds max startup time
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
traffic:
- percent: 100
latestRevision: true
2. Deployment Scripts
#!/bin/bash
# deploy.sh
set -e
# Configuration
PROJECT_ID="your-project-id"
SERVICE_NAME="cloudrun-demo"
REGION="us-central1"
IMAGE_NAME="gcr.io/$PROJECT_ID/$SERVICE_NAME"
echo "Building and deploying $SERVICE_NAME to Cloud Run..."
# Build the container image
echo "Building container image..."
mvn compile jib:build -DskipTests
# Deploy to Cloud Run
echo "Deploying to Cloud Run..."
gcloud run deploy $SERVICE_NAME \
--image $IMAGE_NAME \
--platform managed \
--region $REGION \
--allow-unauthenticated \
--memory 512Mi \
--cpu 1 \
--concurrency 80 \
--max-instances 100 \
--timeout 300s \
--set-env-vars=SPRING_PROFILES_ACTIVE=cloudrun,production \
--update-secrets=DATABASE_URL=cloudrun-db-secret:latest \
--update-secrets=DATABASE_USERNAME=cloudrun-db-secret:latest \
--update-secrets=DATABASE_PASSWORD=cloudrun-db-secret:latest
# Get the service URL
SERVICE_URL=$(gcloud run services describe $SERVICE_NAME --region $REGION --format 'value(status.url)')
echo "Service deployed successfully: $SERVICE_URL"
# Run health check
echo "Running health check..."
curl -f "$SERVICE_URL/actuator/health" || exit 1
echo "Health check passed!"
3. Cloud Build Configuration
# cloudbuild.yaml
steps:
# Build the application
- name: maven:3.9-eclipse-temurin-21
entrypoint: mvn
args: ['clean', 'compile', '-DskipTests']
# Run tests
- name: maven:3.9-eclipse-temurin-21
entrypoint: mvn
args: ['test']
# Build container image
- name: maven:3.9-eclipse-temurin-21
entrypoint: mvn
args: ['compile', 'jib:build', '-DskipTests']
secretEnv: ['GOOGLE_APPLICATION_CREDENTIALS']
# Deploy to Cloud Run
- name: 'gcr.io/cloud-builders/gcloud'
args:
- 'run'
- 'deploy'
- 'cloudrun-demo'
- '--image=gcr.io/$PROJECT_ID/cloudrun-demo:latest'
- '--region=us-central1'
- '--platform=managed'
- '--allow-unauthenticated'
- '--memory=512Mi'
- '--cpu=1'
- '--concurrency=80'
- '--max-instances=100'
- '--timeout=300s'
- '--set-env-vars=SPRING_PROFILES_ACTIVE=cloudrun,production'
- '--update-secrets=DATABASE_URL=cloudrun-db-secret:latest'
- '--update-secrets=DATABASE_USERNAME=cloudrun-db-secret:latest'
- '--update-secrets=DATABASE_PASSWORD=cloudrun-db-secret:latest'
# Secrets configuration
availableSecrets:
secretManager:
- versionName: projects/$PROJECT_ID/secrets/cloudbuild-sa-key/versions/latest
env: 'GOOGLE_APPLICATION_CREDENTIALS'
# Images to store in Container Registry
images:
- 'gcr.io/$PROJECT_ID/cloudrun-demo:latest'
# Timeout and options
timeout: 1800s
options:
logging: CLOUD_LOGGING_ONLY

Testing

1. Unit Tests
package com.example.cloudrun.service;
import com.example.cloudrun.model.User;
import com.example.cloudrun.model.UserStatus;
import com.example.cloudrun.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Optional;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private CloudRunMetrics metrics;
@InjectMocks
private UserService userService;
@Test
void testCreateUser() {
// Given
User user = new User("John", "Doe", "[email protected]");
User savedUser = new User("John", "Doe", "[email protected]");
savedUser.setId(UUID.randomUUID());
when(userRepository.existsByEmail("[email protected]")).thenReturn(false);
when(userRepository.save(any(User.class))).thenReturn(savedUser);
// When
User result = userService.createUser(user);
// Then
assertNotNull(result);
assertNotNull(result.getId());
assertEquals("[email protected]", result.getEmail());
verify(userRepository).existsByEmail("[email protected]");
verify(userRepository).save(user);
verify(metrics).recordUserCreated();
}
@Test
void testGetUserById() {
// Given
UUID userId = UUID.randomUUID();
User user = new User("John", "Doe", "[email protected]");
user.setId(userId);
when(userRepository.findById(userId)).thenReturn(Optional.of(user));
// When
Optional<User> result = userService.getUserById(userId);
// Then
assertTrue(result.isPresent());
assertEquals(userId, result.get().getId());
verify(userRepository).findById(userId);
verify(metrics).recordUserQuery();
}
}
2. Integration Tests
package com.example.cloudrun.controller;
import com.example.cloudrun.model.CreateUserRequest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
private final ObjectMapper objectMapper = new ObjectMapper();
@Test
void testCreateUser() throws Exception {
CreateUserRequest request = new CreateUserRequest();
request.setFirstName("John");
request.setLastName("Doe");
request.setEmail("[email protected]");
mockMvc.perform(post("/api/v1/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.firstName").value("John"))
.andExpect(jsonPath("$.lastName").value("Doe"))
.andExpect(jsonPath("$.email").value("[email protected]"));
}
@Test
void testHealthCheck() throws Exception {
mockMvc.perform(get("/actuator/health"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("UP"));
}
}
3. Test Configuration
# application-test.yml
spring:
datasource:
url: jdbc:tc:postgresql:15:///testdb
username: test
password: test
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
redis:
host: localhost
port: 6379
cloud:
gcp:
enabled: false
logging:
level:
com.example.cloudrun: DEBUG

Performance Optimization

1. Cold Start Optimization
package com.example.cloudrun.optimization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
@Component
@Profile("cloudrun")
public class WarmupRunner implements CommandLineRunner {
private static final Logger LOG = LoggerFactory.getLogger(WarmupRunner.class);
private final DataSource dataSource;
private final JdbcTemplate jdbcTemplate;
private final RedisTemplate<String, String> redisTemplate;
public WarmupRunner(DataSource dataSource, JdbcTemplate jdbcTemplate, 
RedisTemplate<String, String> redisTemplate) {
this.dataSource = dataSource;
this.jdbcTemplate = jdbcTemplate;
this.redisTemplate = redisTemplate;
}
@Override
public void run(String... args) throws Exception {
LOG.info("Starting application warmup...");
// Warm up database connections
warmupDatabase();
// Warm up Redis connection
warmupRedis();
// Warm up any other critical components
warmupComponents();
LOG.info("Application warmup completed");
}
private void warmupDatabase() {
try (Connection connection = dataSource.getConnection()) {
jdbcTemplate.execute("SELECT 1");
LOG.info("Database connection warmed up successfully");
} catch (Exception e) {
LOG.warn("Database warmup failed: {}", e.getMessage());
}
}
private void warmupRedis() {
try {
redisTemplate.opsForValue().get("warmup");
LOG.info("Redis connection warmed up successfully");
} catch (Exception e) {
LOG.warn("Redis warmup failed: {}", e.getMessage());
}
}
private void warmupComponents() {
// Warm up any other components that benefit from initialization
// This could include HTTP clients, thread pools, etc.
LOG.info("Component warmup completed");
}
}
2. Memory Optimization Configuration
package com.example.cloudrun.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class AsyncConfiguration {
@Bean(name = "taskExecutor")
@ConditionalOnProperty(name = "spring.threads.virtual.enabled", havingValue = "false")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// Conservative settings for Cloud Run
executor.setCorePoolSize(2);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("CloudRunAsync-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(30);
executor.initialize();
return executor;
}
}

Monitoring and Logging

1. Structured Logging
package com.example.cloudrun.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;
@Component
public class RequestLoggingFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory.getLogger(RequestLoggingFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, 
HttpServletResponse response, 
FilterChain filterChain) throws ServletException, IOException {
String requestId = UUID.randomUUID().toString();
MDC.put("requestId", requestId);
MDC.put("service", "cloudrun-demo");
long startTime = System.currentTimeMillis();
try {
LOG.info("Request started: {} {} from {}", 
request.getMethod(), 
request.getRequestURI(),
request.getRemoteAddr());
filterChain.doFilter(request, response);
} finally {
long duration = System.currentTimeMillis() - startTime;
LOG.info("Request completed: {} {} - Status: {} - Duration: {}ms", 
request.getMethod(), 
request.getRequestURI(),
response.getStatus(),
duration);
MDC.clear();
}
}
}
2. Custom Metrics
package com.example.cloudrun.metrics;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class RuntimeMetrics {
private final AtomicInteger activeRequests = new AtomicInteger(0);
private final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
public RuntimeMetrics(MeterRegistry meterRegistry) {
// Active requests gauge
Gauge.builder("cloudrun.runtime.active.requests")
.description("Number of active HTTP requests")
.register(meterRegistry, activeRequests, AtomicInteger::get);
// Memory usage gauge
Gauge.builder("cloudrun.runtime.memory.used")
.description("Used memory in bytes")
.register(meterRegistry, this, RuntimeMetrics::getUsedMemory);
// Available memory gauge
Gauge.builder("cloudrun.runtime.memory.max")
.description("Max memory in bytes")
.register(meterRegistry, this, RuntimeMetrics::getMaxMemory);
}
public void incrementActiveRequests() {
activeRequests.incrementAndGet();
}
public void decrementActiveRequests() {
activeRequests.decrementAndGet();
}
private long getUsedMemory() {
return memoryMXBean.getHeapMemoryUsage().getUsed() + 
memoryMXBean.getNonHeapMemoryUsage().getUsed();
}
private long getMaxMemory() {
return memoryMXBean.getHeapMemoryUsage().getMax() + 
memoryMXBean.getNonHeapMemoryUsage().getMax();
}
@Scheduled(fixedRate = 30000) // Every 30 seconds
public void logRuntimeStats() {
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long maxMemory = runtime.maxMemory();
LOG.info("Runtime stats - Memory: {}/{} MB, Active requests: {}", 
usedMemory / 1024 / 1024, 
maxMemory / 1024 / 1024,
activeRequests.get());
}
}

Best Practices

1. Environment-specific Configuration
# application-production.yml
spring:
datasource:
hikari:
maximum-pool-size: 10
minimum-idle: 2
connection-timeout: 30000
max-lifetime: 1800000
idle-timeout: 600000
redis:
lettuce:
pool:
max-active: 16
max-idle: 8
min-idle: 2
jpa:
properties:
hibernate:
connection:
provider_disables_autocommit: true
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: never
metrics:
enabled: true
logging:
level:
com.example.cloudrun: INFO
org.hibernate: WARN
org.springframework: INFO
# Cloud Run specific optimizations
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
threads:
virtual:
enabled: true
2. Security Configuration
package com.example.cloudrun.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// Cloud Run handles SSL termination
.requiresChannel(channel -> channel.anyRequest().requiresSecure())
// CORS configuration
.cors(cors -> cors.configure(http))
// CSRF configuration (stateless API)
.csrf(csrf -> csrf.disable())
// Authorization rules
.authorizeHttpRequests(authz -> authz
.requestMatchers("/actuator/health", "/actuator/info").permitAll()
.requestMatchers("/api/v1/**").authenticated()
.anyRequest().denyAll()
);
return http.build();
}
}

Conclusion

Building Java applications for Google Cloud Run provides:

  • Serverless operation with automatic scaling
  • Cost efficiency with pay-per-use pricing
  • Zero infrastructure management
  • Fast deployment and iteration cycles
  • Integration with Google Cloud services

Key optimization strategies covered:

  1. Container optimization with Jib and efficient base images
  2. Cold start reduction through warmup and JVM tuning
  3. Resource management with appropriate memory and CPU limits
  4. Health checks for reliable deployment and scaling
  5. Monitoring and logging with Cloud Operations
  6. Database and cache integration for performance
  7. Security best practices for production deployment

This architecture enables building highly scalable, cost-effective Java applications that automatically scale based on demand while maintaining excellent performance and reliability.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper