diff --git a/services/reports/src/constants.rs b/services/reports/src/constants.rs
--- a/services/reports/src/constants.rs
+++ b/services/reports/src/constants.rs
@@ -1,2 +1,14 @@
 pub const REPORT_LIST_DEFAULT_PAGE_SIZE: u32 = 20;
 pub const REQUEST_BODY_JSON_SIZE_LIMIT: usize = 10 * 1024 * 1024; // 10MB
+
+pub fn max_report_size() -> usize {
+  let size = std::env::var("MAX_REPORT_SIZE")
+    .ok()
+    .and_then(|s| s.parse::<usize>().ok())
+    .unwrap_or(REQUEST_BODY_JSON_SIZE_LIMIT);
+
+  if size != REQUEST_BODY_JSON_SIZE_LIMIT {
+    tracing::info!("MAX_REPORT_SIZE is set to {} bytes", size);
+  }
+  size
+}
diff --git a/services/reports/src/http/mod.rs b/services/reports/src/http/mod.rs
--- a/services/reports/src/http/mod.rs
+++ b/services/reports/src/http/mod.rs
@@ -9,7 +9,7 @@
 use tracing::{debug, error, info, trace, warn};
 
 use crate::config::CONFIG;
-use crate::constants::REQUEST_BODY_JSON_SIZE_LIMIT;
+use crate::constants::max_report_size;
 use crate::service::{ReportsService, ReportsServiceError};
 
 mod handlers;
@@ -27,8 +27,7 @@
     CONFIG.http_port
   );
   HttpServer::new(move || {
-    let json_cfg =
-      web::JsonConfig::default().limit(REQUEST_BODY_JSON_SIZE_LIMIT);
+    let json_cfg = web::JsonConfig::default().limit(max_report_size());
     App::new()
       .app_data(json_cfg)
       .app_data(reports_service.to_owned())
diff --git a/services/terraform/remote/service_reports.tf b/services/terraform/remote/service_reports.tf
--- a/services/terraform/remote/service_reports.tf
+++ b/services/terraform/remote/service_reports.tf
@@ -1,5 +1,5 @@
 locals {
-  reports_service_image_tag           = local.is_staging ? "latest" : "0.1.0"
+  reports_service_image_tag           = local.is_staging ? "latest" : "0.1.1"
   reports_service_container_name      = "reports-service-server"
   reports_service_server_image        = "commapp/reports-server:${local.reports_service_image_tag}"
   reports_service_container_http_port = 50056
@@ -34,6 +34,10 @@
           name  = "RUST_LOG"
           value = "info"
         },
+        {
+          name  = "MAX_REPORT_SIZE"
+          value = "314572800" # 300MB
+        },
         {
           name  = "PUBLIC_URL",
           value = "https://${local.reports_service_domain_name}"
@@ -66,10 +70,10 @@
   ])
   task_role_arn            = aws_iam_role.reports_service.arn
   execution_role_arn       = aws_iam_role.ecs_task_execution.arn
-  network_mode             = "bridge"
-  cpu                      = "256"
-  memory                   = "256"
-  requires_compatibilities = ["EC2"]
+  network_mode             = "awsvpc"
+  cpu                      = "1024"
+  memory                   = "8192"
+  requires_compatibilities = ["EC2", "FARGATE"]
 
   # Set this to true if you want to keep old revisions
   # when this definition is changed
@@ -79,7 +83,7 @@
 resource "aws_ecs_service" "reports_service" {
   name        = "reports-service"
   cluster     = aws_ecs_cluster.comm_services.id
-  launch_type = "EC2"
+  launch_type = "FARGATE"
 
   task_definition      = aws_ecs_task_definition.reports_service.arn
   force_new_deployment = true
@@ -101,6 +105,18 @@
     container_port   = local.reports_service_container_http_port
   }
 
+  network_configuration {
+    assign_public_ip = true
+    security_groups = [
+      aws_security_group.reports_service.id,
+    ]
+    subnets = [
+      aws_subnet.public_a.id,
+      aws_subnet.public_b.id,
+      aws_subnet.public_c.id,
+    ]
+  }
+
   deployment_circuit_breaker {
     enable   = true
     rollback = true
@@ -143,7 +159,7 @@
   vpc_id   = aws_vpc.default.id
 
   # ECS Fargate requires target type set to IP
-  target_type = "instance"
+  target_type = "ip"
 
   health_check {
     enabled             = true
@@ -181,9 +197,6 @@
   }
 
   lifecycle {
-    # Target group cannot be destroyed if it is used
-    replace_triggered_by = [aws_lb_target_group.reports_service_http]
-
     # Required to avoid no-op plan differences
     ignore_changes = [default_action[0].forward[0].stickiness[0].duration]
   }