Repository: superfly/flyctl
Pull request #4085 (2024-11-26 17:28:38)

Add deploy.release_command_vm directive to set release machine size
### Change Summary

What and Why: There is no way to explicitly set the release command machine size

How: Add `[deploy.release_command_vm]` directive similar to `[[vm]]` 

```
[deploy]
  release_command = "run something for me"
  [deploy.release_command_vm]
    size = "performance-1x"
    memory = "8gb"
```

---

### Documentation

- [ ] Fresh Produce
- [ ] In superfly/docs, or asked for help from docs team
- [ ] n/a



Patch:
diff --git a/.github/workflows/preflight.yml b/.github/workflows/preflight.yml
index 4ca5df200..95f0ff498 100644
--- a/.github/workflows/preflight.yml
+++ b/.github/workflows/preflight.yml
@@ -18,8 +18,7 @@ jobs:
       fail-fast: false
       matrix:
         parallelism: [20]
-        index:
-          [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
+        index: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
     steps:
       - uses: actions/checkout@v4
       - uses: actions/setup-go@v5
@@ -57,10 +56,11 @@ jobs:
           ./scripts/preflight.sh -r "${{ github.ref }}" -t "${{ matrix.parallelism }}" -i "${{ matrix.index }}" -o $GITHUB_OUTPUT
       - name: Post failure to slack
         if: ${{ github.ref == 'refs/heads/master' && failure() }}
-        uses: slackapi/slack-github-action@485a9d42d3a73031f12ec201c457e2162c45d02d
+        uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0
+        env:
+          SLACK_WEBHOOK_URL: ${{ secrets.PREFLIGHT_SLACK_WEBHOOK_URL }}
+          SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
         with:
-          webhook: ${{ secrets.PREFLIGHT_SLACK_WEBHOOK_URL }}
-          webhook-type: incoming-webhook
           payload: |
             {
               "blocks": [
diff --git a/.github/workflows/preflight_cleanup.yml b/.github/workflows/preflight_cleanup.yml
index c38a36a5f..146b7637a 100644
--- a/.github/workflows/preflight_cleanup.yml
+++ b/.github/workflows/preflight_cleanup.yml
@@ -33,10 +33,11 @@ jobs:
           go run ./scripts/clean-up-preflight-apps
       - name: Post failure to slack
         if: ${{ github.ref == 'refs/heads/master' && failure() }}
-        uses: slackapi/slack-github-action@485a9d42d3a73031f12ec201c457e2162c45d02d
+        uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0
+        env:
+          SLACK_WEBHOOK_URL: ${{ secrets.PREFLIGHT_SLACK_WEBHOOK_URL }}
+          SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
         with:
-          webhook: ${{ secrets.PREFLIGHT_SLACK_WEBHOOK_URL }}
-          webhook-type: incoming-webhook
           payload: |
             {
               "blocks": [
diff --git a/.github/workflows/preflight_gpu.yml b/.github/workflows/preflight_gpu.yml
index e54e6d92f..faff8f19b 100644
--- a/.github/workflows/preflight_gpu.yml
+++ b/.github/workflows/preflight_gpu.yml
@@ -49,8 +49,7 @@ jobs:
         # Add "a10" to this list once capacity increases.
         vm_size: ["a100-40gb", "l40s"]
         parallelism: [20]
-        index:
-          [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
+        index: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
     steps:
       - uses: actions/checkout@v4
       - uses: actions/setup-go@v5
@@ -86,10 +85,11 @@ jobs:
           ./scripts/preflight.sh -r "${{ github.ref }}" -t "${{ matrix.parallelism }}" -i "${{ matrix.index }}" -o $GITHUB_OUTPUT
       - name: Post failure to slack
         if: ${{ github.ref == 'refs/heads/master' && failure() }}
-        uses: slackapi/slack-github-action@485a9d42d3a73031f12ec201c457e2162c45d02d
+        uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0
+        env:
+          SLACK_WEBHOOK_URL: ${{ secrets.PREFLIGHT_SLACK_WEBHOOK_URL }}
+          SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
         with:
-          webhook: ${{ secrets.PREFLIGHT_SLACK_WEBHOOK_URL }}
-          webhook-type: incoming-webhook
           payload: |
             {
               "blocks": [
diff --git a/agent/client.go b/agent/client.go
index 0477d93c0..899ea9b3c 100644
--- a/agent/client.go
+++ b/agent/client.go
@@ -34,7 +34,7 @@ import (
 )
 
 // Establish starts the daemon, if necessary, and returns a client to it.
-func Establish(ctx context.Context, apiClient wireguard.WebClient) (*Client, error) {
+func Establish(ctx context.Context, apiClient flyutil.Client) (*Client, error) {
 	if err := wireguard.PruneInvalidPeers(ctx, apiClient); err != nil {
 		return nil, err
 	}
diff --git a/go.mod b/go.mod
index ad9e1b45b..fe45eee14 100644
--- a/go.mod
+++ b/go.mod
@@ -1,8 +1,8 @@
 module github.com/superfly/flyctl
 
-go 1.22.7
+go 1.22.0
 
-toolchain go1.22.9
+toolchain go1.22.6
 
 require (
 	github.com/AlecAivazis/survey/v2 v2.3.7
@@ -13,9 +13,9 @@ require (
 	github.com/PuerkitoBio/rehttp v1.4.0
 	github.com/alecthomas/chroma v0.10.0
 	github.com/avast/retry-go/v4 v4.6.0
-	github.com/aws/aws-sdk-go-v2/config v1.28.5
-	github.com/aws/aws-sdk-go-v2/credentials v1.17.46
-	github.com/aws/aws-sdk-go-v2/service/s3 v1.68.0
+	github.com/aws/aws-sdk-go-v2/config v1.28.1
+	github.com/aws/aws-sdk-go-v2/credentials v1.17.42
+	github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2
 	github.com/azazeal/pause v1.3.0
 	github.com/blang/semver v3.5.1+incompatible
 	github.com/briandowns/spinner v1.23.1
@@ -25,7 +25,7 @@ require (
 	github.com/chzyer/readline v1.5.1
 	github.com/cli/safeexec v1.0.1
 	github.com/coder/websocket v1.8.12
-	github.com/containerd/continuity v0.4.5
+	github.com/containerd/continuity v0.4.4
 	github.com/depot/depot-go v0.5.0
 	github.com/docker/docker v27.3.1+incompatible
 	github.com/docker/go-connections v0.5.0
@@ -74,30 +74,30 @@ require (
 	github.com/spf13/cobra v1.8.1
 	github.com/spf13/pflag v1.0.5
 	github.com/spf13/viper v1.19.0
-	github.com/stretchr/testify v1.10.0
-	github.com/superfly/fly-go v0.1.35
+	github.com/stretchr/testify v1.9.0
+	github.com/superfly/fly-go v0.1.34
 	github.com/superfly/graphql v0.2.4
 	github.com/superfly/lfsc-go v0.1.1
 	github.com/superfly/macaroon v0.2.14-0.20240819201738-61a02aa53648
 	github.com/superfly/tokenizer v0.0.3-0.20240826174224-a17a2e0a9dc0
-	github.com/vektah/gqlparser/v2 v2.5.19
-	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0
-	go.opentelemetry.io/otel v1.32.0
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0
-	go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0
-	go.opentelemetry.io/otel/sdk v1.32.0
-	go.opentelemetry.io/otel/trace v1.32.0
-	golang.org/x/crypto v0.29.0
+	github.com/vektah/gqlparser/v2 v2.5.18
+	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0
+	go.opentelemetry.io/otel v1.31.0
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0
+	go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0
+	go.opentelemetry.io/otel/sdk v1.31.0
+	go.opentelemetry.io/otel/trace v1.31.0
+	golang.org/x/crypto v0.28.0
 	golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
-	golang.org/x/mod v0.22.0
-	golang.org/x/net v0.31.0
-	golang.org/x/sync v0.9.0
-	golang.org/x/sys v0.27.0
-	golang.org/x/term v0.26.0
-	golang.org/x/text v0.20.0
-	golang.org/x/time v0.8.0
+	golang.org/x/mod v0.21.0
+	golang.org/x/net v0.30.0
+	golang.org/x/sync v0.8.0
+	golang.org/x/sys v0.26.0
+	golang.org/x/term v0.25.0
+	golang.org/x/text v0.19.0
+	golang.org/x/time v0.7.0
 	golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
-	google.golang.org/grpc v1.68.0
+	google.golang.org/grpc v1.67.1
 	gopkg.in/yaml.v2 v2.4.0
 	gopkg.in/yaml.v3 v3.0.1
 )
@@ -127,23 +127,23 @@ require (
 	github.com/alexflint/go-scalar v1.2.0 // indirect
 	github.com/andybalholm/brotli v1.1.0 // indirect
 	github.com/apex/log v1.9.0 // indirect
-	github.com/aws/aws-sdk-go-v2 v1.32.5 // indirect
-	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect
-	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect
+	github.com/aws/aws-sdk-go-v2 v1.32.3 // indirect
+	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 // indirect
+	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.24 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.22 // indirect
 	github.com/aws/aws-sdk-go-v2/service/ecr v1.24.5 // indirect
 	github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.5 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.5 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 // indirect
-	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 // indirect
-	github.com/aws/smithy-go v1.22.1 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.3 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.3 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 // indirect
+	github.com/aws/smithy-go v1.22.0 // indirect
 	github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231213181459-b0fcec718dc6 // indirect
 	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
@@ -183,7 +183,7 @@ require (
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/gogo/googleapis v1.4.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
+	github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/google/btree v1.0.1 // indirect
@@ -192,7 +192,7 @@ require (
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/gorilla/mux v1.8.0 // indirect
 	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
-	github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
 	github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
@@ -258,17 +258,17 @@ require (
 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
 	github.com/xeipuuv/gojsonschema v1.2.0 // indirect
 	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect
-	go.opentelemetry.io/otel/metric v1.32.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect
+	go.opentelemetry.io/otel/metric v1.31.0 // indirect
 	go.opentelemetry.io/proto/otlp v1.3.1 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
-	golang.org/x/oauth2 v0.23.0 // indirect
+	golang.org/x/oauth2 v0.22.0 // indirect
 	golang.org/x/tools v0.26.0 // indirect
 	golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
 	google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
 	google.golang.org/protobuf v1.35.1 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
diff --git a/go.sum b/go.sum
index 75dd56c11..1f0ed0636 100644
--- a/go.sum
+++ b/go.sum
@@ -96,46 +96,46 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY
 github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA=
 github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE=
 github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo=
-github.com/aws/aws-sdk-go-v2 v1.32.5/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc=
-github.com/aws/aws-sdk-go-v2/config v1.28.5 h1:Za41twdCXbuyyWv9LndXxZZv3QhTG1DinqlFsSuvtI0=
-github.com/aws/aws-sdk-go-v2/config v1.28.5/go.mod h1:4VsPbHP8JdcdUDmbTVgNL/8w9SqOkM5jyY8ljIxLO3o=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.46 h1:AU7RcriIo2lXjUfHFnFKYsLCwgbz1E7Mm95ieIRDNUg=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.46/go.mod h1:1FmYyLGL08KQXQ6mcTlifyFXfJVCNJTVGuQP4m0d/UA=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 h1:sDSXIrlsFSFJtWKLQS4PUWRvrT580rrnuLydJrCQ/yA=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20/go.mod h1:WZ/c+w0ofps+/OUqMwWgnfrgzZH1DZO1RIkktICsqnY=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 h1:4usbeaes3yJnCFC7kfeyhkdkPtoRYPa/hTmCqMpKpLI=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24/go.mod h1:5CI1JemjVwde8m2WG3cz23qHKPOxbpkq0HaoreEgLIY=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 h1:N1zsICrQglfzaBnrfM0Ys00860C+QFwu6u/5+LomP+o=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24/go.mod h1:dCn9HbJ8+K31i8IQ8EWmWj0EiIk0+vKiHNMxTTYveAg=
+github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk=
+github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 h1:pT3hpW0cOHRJx8Y0DfJUEQuqPild8jRGmSFmBgvydr0=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6/go.mod h1:j/I2++U0xX+cr44QjHay4Cvxj6FUbnxrgmqN3H1jTZA=
+github.com/aws/aws-sdk-go-v2/config v1.28.1 h1:oxIvOUXy8x0U3fR//0eq+RdCKimWI900+SV+10xsCBw=
+github.com/aws/aws-sdk-go-v2/config v1.28.1/go.mod h1:bRQcttQJiARbd5JZxw6wG0yIK3eLeSCPdg6uqmmlIiI=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.42 h1:sBP0RPjBU4neGpIYyx8mkU2QqLPl5u9cmdTWVzIpHkM=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.42/go.mod h1:FwZBfU530dJ26rv9saAbxa9Ej3eF/AK0OAY86k13n4M=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 h1:68jFVtt3NulEzojFesM/WVarlFpCaXLKaBxDpzkQ9OQ=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18/go.mod h1:Fjnn5jQVIo6VyedMc0/EhPpfNlPl7dHV916O6B+49aE=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 h1:Jw50LwEkVjuVzE1NzkhNKkBf9cRN7MtE1F/b2cOKTUM=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22/go.mod h1:Y/SmAyPcOTmpeVaWSzSKiILfXTVJwrGmYZhcRbhWuEY=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 h1:981MHwBaRZM7+9QSR6XamDzF/o7ouUGxFzr+nVSIhrs=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22/go.mod h1:1RA1+aBEfn+CAB/Mh0MB6LsdCYCnjZm7tKXtnk499ZQ=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.24 h1:JX70yGKLj25+lMC5Yyh8wBtvB01GDilyRuJvXJ4piD0=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.24/go.mod h1:+Ln60j9SUTD0LEwnhEB0Xhg61DHqplBrbZpLgyjoEHg=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.22 h1:yV+hCAHZZYJQcwAaszoBNwLbPItHvApxT0kVIw6jRgs=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.22/go.mod h1:kbR1TL8llqB1eGnVbybcA4/wgScxdylOdyAd51yxPdw=
 github.com/aws/aws-sdk-go-v2/service/ecr v1.24.5 h1:wLPDAUFT50NEXGXpywRU3AA74pg35RJjWol/68ruvQQ=
 github.com/aws/aws-sdk-go-v2/service/ecr v1.24.5/go.mod h1:AOHmGMoPtSY9Zm2zBuwUJQBisIvYAZeA1n7b6f4e880=
 github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5 h1:PQp21GBlGNaQ+AVJAB8w2KTmLx0DkFS2fDET2Iy3+f0=
 github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.21.5/go.mod h1:WMntdAol8KgeYsa5sDZPsRTXs4jVZIMYu0eQVVIQxnc=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.5 h1:gvZOjQKPxFXy1ft3QnEyXmT+IqneM9QAUWlM3r0mfqw=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.5/go.mod h1:DLWnfvIcm9IET/mmjdxeXbBKmTCm0ZB8p1za9BVteM8=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 h1:wtpJ4zcwrSbwhECWQoI/g6WM9zqCcSpHDJIWSbMLOu4=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5/go.mod h1:qu/W9HXQbbQ4+1+JcZp0ZNPV31ym537ZJN+fiS7Ti8E=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.5 h1:P1doBzv5VEg1ONxnJss1Kh5ZG/ewoIE4MQtKKc6Crgg=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.5/go.mod h1:NOP+euMW7W3Ukt28tAxPuoWao4rhhqJD3QEBk7oCg7w=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.68.0 h1:bFpcqdwtAEsgpZXvkTxIThFQx/EM0oV6kXmfFIGjxME=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.68.0/go.mod h1:ralv4XawHjEMaHOWnTFushl0WRqim/gQWesAMF6hTow=
-github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 h1:3zu537oLmsPfDMyjnUS2g+F2vITgy5pB74tHI+JBNoM=
-github.com/aws/aws-sdk-go-v2/service/sso v1.24.6/go.mod h1:WJSZH2ZvepM6t6jwu4w/Z45Eoi75lPN7DcydSRtJg6Y=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 h1:K0OQAsDywb0ltlFrZm0JHPY3yZp/S9OaoLU33S7vPS8=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5/go.mod h1:ORITg+fyuMoeiQFiVGoqB3OydVTLkClw/ljbblMq6Cc=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 h1:6SZUVRQNvExYlMLbHdlKB48x0fLbc2iVROyaNEwBHbU=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.1/go.mod h1:GqWyYCwLXnlUB1lOAXQyNSPqPLQJvmo8J0DWBzp9mtg=
-github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
-github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.3 h1:kT6BcZsmMtNkP/iYMcRG+mIEA/IbeiUimXtGmqF39y0=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.3/go.mod h1:Z8uGua2k4PPaGOYn66pK02rhMrot3Xk3tpBuUFPomZU=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 h1:qcxX0JYlgWH3hpPUnd6U0ikcl6LLA9sLkXE2w1fpMvY=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3/go.mod h1:cLSNEmI45soc+Ef8K/L+8sEA3A3pYFEYf5B5UI+6bH4=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.3 h1:ZC7Y/XgKUxwqcdhO5LE8P6oGP1eh6xlQReWNKfhvJno=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.3/go.mod h1:WqfO7M9l9yUAw0HcHaikwRd/H6gzYdz7vjejCA5e2oY=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2 h1:p9TNFL8bFUMd+38YIpTAXpoxyz0MxC7FlbFEH4P4E1U=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2/go.mod h1:fNjyo0Coen9QTwQLWeV6WO2Nytwiu+cCcWaTdKCAqqE=
+github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 h1:UTpsIf0loCIWEbrqdLb+0RxnTXfWh2vhw4nQmFi4nPc=
+github.com/aws/aws-sdk-go-v2/service/sso v1.24.3/go.mod h1:FZ9j3PFHHAR+w0BSEjK955w5YD2UwB/l/H0yAK3MJvI=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 h1:2YCmIXv3tmiItw0LlYf6v7gEHebLY45kBEnPezbUKyU=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3/go.mod h1:u19stRyNPxGhj6dRm+Cdgu6N75qnbW7+QN0q0dsAk58=
+github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 h1:wVnQ6tigGsRqSWDEEyH6lSAJ9OyFUsSnbaUWChuSGzs=
+github.com/aws/aws-sdk-go-v2/service/sts v1.32.3/go.mod h1:VZa9yTFyj4o10YGsmDO4gbQJUvvhY72fhumT8W4LqsE=
+github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
+github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
 github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231213181459-b0fcec718dc6 h1:PlJRmqKlSlEUlwem1c3zdPaEMtJc/ktnV7naD5Qvsx4=
 github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231213181459-b0fcec718dc6/go.mod h1:08sPJIlDHu4HwQ1xScPgsBWezvM6U10ghGKBJu0mowA=
 github.com/aybabtme/iocontrol v0.0.0-20150809002002-ad15bcfc95a0 h1:0NmehRCgyk5rljDQLKUO+cRJCnduDyn11+zGZIc9Z48=
@@ -202,8 +202,8 @@ github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn
 github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
 github.com/containerd/containerd v1.7.16 h1:7Zsfe8Fkj4Wi2My6DXGQ87hiqIrmOXolm72ZEkFU5Mg=
 github.com/containerd/containerd v1.7.16/go.mod h1:NL49g7A/Fui7ccmxV6zkBWwqMgmMxFWzujYCc+JLt7k=
-github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
-github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
+github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII=
+github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
 github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
 github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
 github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
@@ -322,9 +322,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
+github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
-github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
-github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -362,8 +361,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
 github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
 github.com/haileys/go-harlog v0.0.0-20230517070437-0f99204b5a57 h1:m7J0Y9Bqry85LVC3uboOp9sDUnH29o85FB5ZoSa3fTg=
 github.com/haileys/go-harlog v0.0.0-20230517070437-0f99204b5a57/go.mod h1:feJwxrNkN8pzC59AtacOExfyTP4Z5Z44hlgNYEG+KKM=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -639,12 +638,12 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
-github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
-github.com/superfly/fly-go v0.1.35 h1:jmreAJxXcWChIkBNe4ICq5TFEIDT7DB0kItYcjo86ts=
-github.com/superfly/fly-go v0.1.35/go.mod h1:ZLXEOA1TpJz89A3tUBpzW3/foX+HGLaVtudSeUymj/w=
+github.com/superfly/fly-go v0.1.34 h1:h6tL+z8VqMH4yHM+YwbC8fib3PquUB3IrGu+0cxuFNw=
+github.com/superfly/fly-go v0.1.34/go.mod h1:ZLXEOA1TpJz89A3tUBpzW3/foX+HGLaVtudSeUymj/w=
 github.com/superfly/graphql v0.2.4 h1:Av8hSk4x8WvKJ6MTnEwrLknSVSGPc7DWpgT3z/kt3PU=
 github.com/superfly/graphql v0.2.4/go.mod h1:CVfDl31srm8HnJ9udwLu6hFNUW/P6GUM2dKcG1YQ8jc=
 github.com/superfly/lfsc-go v0.1.1 h1:dGjLgt81D09cG+aR9lJZIdmonjZSR5zYCi7s54+ZU2Q=
@@ -670,8 +669,8 @@ github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 h1:Y/M5lygoNPKwVN
 github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
 github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
 github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
-github.com/vektah/gqlparser/v2 v2.5.19 h1:bhCPCX1D4WWzCDvkPl4+TP1N8/kLrWnp43egplt7iSg=
-github.com/vektah/gqlparser/v2 v2.5.19/go.mod h1:y7kvl5bBlDeuWIvLtA9849ncyvx6/lj06RsMrEjVy3U=
+github.com/vektah/gqlparser/v2 v2.5.18 h1:zSND3GtutylAQ1JpWnTHcqtaRZjl+y3NROeW8vuNo6Y=
+github.com/vektah/gqlparser/v2 v2.5.18/go.mod h1:6HLzf7JKv9Fi3APymudztFQNmLXR5qJeEo6BOFcXVfc=
 github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
 github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
 github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
@@ -692,22 +691,22 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
 go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94=
-go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
-go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 h1:cC2yDI3IQd0Udsux7Qmq8ToKAx1XCilTQECZ0KDZyTw=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0/go.mod h1:2PD5Ex6z8CFzDbTdOlwyNIUywRr1DN0ospafJM1wJ+s=
-go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
-go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
-go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
-go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
-go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
-go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
+go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
+go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 h1:UGZ1QwZWY67Z6BmckTU+9Rxn04m2bD3gD6Mk0OIOCPk=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0/go.mod h1:fcwWuDuaObkkChiDlhEpSq9+X1C0omv+s5mBtToAQ64=
+go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
+go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
+go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
+go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
+go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
+go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
 go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
 go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -728,8 +727,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz
 golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
 golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
 golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
-golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
-golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
+golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
+golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
 golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
@@ -744,8 +743,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
-golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
+golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -765,11 +764,11 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
-golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
+golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
+golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
-golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
+golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -778,8 +777,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
-golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
+golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -811,8 +810,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
-golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
+golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -823,8 +822,8 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
 golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
-golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
-golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
+golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
+golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -835,10 +834,10 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
-golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
-golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
-golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
+golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
+golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -868,17 +867,17 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
 google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
 google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
-google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g=
-google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
+google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
+google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
-google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
+google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
+google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
diff --git a/internal/appconfig/config.go b/internal/appconfig/config.go
index 69329d051..a1bab067a 100644
--- a/internal/appconfig/config.go
+++ b/internal/appconfig/config.go
@@ -87,11 +87,12 @@ type Metrics struct {
 }
 
 type Deploy struct {
-	ReleaseCommand        string        `toml:"release_command,omitempty" json:"release_command,omitempty"`
-	ReleaseCommandTimeout *fly.Duration `toml:"release_command_timeout,omitempty" json:"release_command_timeout,omitempty"`
 	Strategy              string        `toml:"strategy,omitempty" json:"strategy,omitempty"`
 	MaxUnavailable        *float64      `toml:"max_unavailable,omitempty" json:"max_unavailable,omitempty"`
 	WaitTimeout           *fly.Duration `toml:"wait_timeout,omitempty" json:"wait_timeout,omitempty"`
+	ReleaseCommand        string        `toml:"release_command,omitempty" json:"release_command,omitempty"`
+	ReleaseCommandTimeout *fly.Duration `toml:"release_command_timeout,omitempty" json:"release_command_timeout,omitempty"`
+	ReleaseCommandCompute *Compute      `toml:"release_command_vm,omitempty" json:"release_command_vm,omitempty"`
 }
 
 type File struct {
@@ -162,8 +163,6 @@ type Experimental struct {
 	EnableEtcd     bool     `toml:"enable_etcd,omitempty" json:"enable_etcd,omitempty"`
 	LazyLoadImages bool     `toml:"lazy_load_images,omitempty" json:"lazy_load_images,omitempty"`
 	Attached       Attached `toml:"attached,omitempty" json:"attached,omitempty"`
-	MachineConfig  string   `toml:"machine_config,omitempty" json:"machine_config,omitempty"`
-	UseZstd        bool     `toml:"use_zstd,omitempty" json:"use_zstd,omitempty"`
 }
 
 type Attached struct {
diff --git a/internal/appconfig/machines.go b/internal/appconfig/machines.go
index f7bc69b50..90c90ce49 100644
--- a/internal/appconfig/machines.go
+++ b/internal/appconfig/machines.go
@@ -1,11 +1,7 @@
 package appconfig
 
 import (
-	"encoding/json"
 	"fmt"
-	"io"
-	"os"
-	"strings"
 
 	"github.com/docker/go-units"
 	"github.com/google/shlex"
@@ -67,6 +63,15 @@ func (c *Config) ToReleaseMachineConfig() (*fly.MachineConfig, error) {
 	mConfig.Files = nil
 	fly.MergeFiles(mConfig, c.MergedFiles)
 
+	// Guest
+	if v := c.Deploy.ReleaseCommandCompute; v != nil {
+		guest, err := c.computeToGuest(v)
+		if err != nil {
+			return nil, err
+		}
+		mConfig.Guest = guest
+	}
+
 	return mConfig, nil
 }
 
@@ -150,9 +155,7 @@ func (c *Config) ToTestMachineConfig(svc *ServiceMachineCheck, origMachine *fly.
 	}
 
 	if c.Experimental != nil {
-		if v := c.Experimental.Entrypoint; v != nil {
-			mConfig.Init.Entrypoint = v
-		}
+		mConfig.Init.Entrypoint = c.Experimental.Entrypoint
 	}
 
 	mConfig.Env["FLY_TEST_COMMAND"] = "1"
@@ -241,30 +244,6 @@ func (c *Config) updateMachineConfig(src *fly.MachineConfig) (*fly.MachineConfig
 		mConfig = helpers.Clone(src)
 	}
 
-	if c.Experimental != nil && len(c.Experimental.MachineConfig) > 0 {
-		emc := c.Experimental.MachineConfig
-		var buf []byte
-		switch {
-		case strings.HasPrefix(emc, "{"):
-			buf = []byte(emc)
-		case strings.HasSuffix(emc, ".json"):
-			fo, err := os.Open(emc)
-			if err != nil {
-				return nil, err
-			}
-			buf, err = io.ReadAll(fo)
-			if err != nil {
-				return nil, err
-			}
-		default:
-			return nil, fmt.Errorf("invalid machine config source: %q", emc)
-		}
-
-		if err := json.Unmarshal(buf, mConfig); err != nil {
-			return nil, fmt.Errorf("invalid machine config %q: %w", emc, err)
-		}
-	}
-
 	// Metrics
 	mConfig.Metrics = nil
 	if len(c.Metrics) > 0 {
@@ -436,8 +415,10 @@ func (c *Config) toMachineGuest() (*fly.MachineGuest, error) {
 	}
 
 	// At most one compute after group flattening
-	compute := c.Compute[0]
+	return c.computeToGuest(c.Compute[0])
+}
 
+func (c *Config) computeToGuest(compute *Compute) (*fly.MachineGuest, error) {
 	size := fly.DefaultVMSize
 	switch {
 	case compute.Size != "":
diff --git a/internal/appconfig/processgroups.go b/internal/appconfig/processgroups.go
index 9386ce719..d7cf5111e 100644
--- a/internal/appconfig/processgroups.go
+++ b/internal/appconfig/processgroups.go
@@ -162,7 +162,6 @@ func (c *Config) Flatten(groupName string) (*Config, error) {
 		dst.Metrics[i].Processes = []string{groupName}
 	}
 
-	// [[restart]]
 	dst.Restart = lo.Filter(dst.Restart, func(x Restart, _ int) bool {
 		return matchesGroups(x.Processes)
 	})
diff --git a/internal/build/imgsrc/depot.go b/internal/build/imgsrc/depot.go
index 23ecb077f..99b90eca9 100644
--- a/internal/build/imgsrc/depot.go
+++ b/internal/build/imgsrc/depot.go
@@ -256,12 +256,6 @@ func buildImage(ctx context.Context, buildkitClient *client.Client, opts ImageOp
 		exportEntry.Attrs["push"] = "true"
 	}
 
-	if opts.UseZstd {
-		exportEntry.Attrs["compression"] = "zstd"
-		exportEntry.Attrs["compression-level"] = "3"
-		exportEntry.Attrs["force-compression"] = "true"
-	}
-
 	ch := make(chan *client.SolveStatus)
 	eg, ctx := errgroup.WithContext(ctx)
 	eg.Go(func() error {
diff --git a/internal/build/imgsrc/resolver.go b/internal/build/imgsrc/resolver.go
index 052661569..372d08315 100644
--- a/internal/build/imgsrc/resolver.go
+++ b/internal/build/imgsrc/resolver.go
@@ -54,7 +54,6 @@ type ImageOptions struct {
 	BuildpacksDockerHost string
 	BuildpacksVolumes    []string
 	UseOverlaybd         bool
-	UseZstd              bool
 }
 
 func (io ImageOptions) ToSpanAttributes() []attribute.KeyValue {
@@ -73,7 +72,6 @@ func (io ImageOptions) ToSpanAttributes() []attribute.KeyValue {
 		attribute.String("imageoptions.buildpacks_docker_host", io.BuildpacksDockerHost),
 		attribute.StringSlice("imageoptions.buildpacks", io.Buildpacks),
 		attribute.StringSlice("imageoptions.buildpacks_volumes", io.BuildpacksVolumes),
-		attribute.Bool("imageoptions.use_zstd", io.UseZstd),
 	}
 
 	if io.BuildArgs != nil {
diff --git a/internal/command/command.go b/internal/command/command.go
index 4251b4641..25fee40db 100644
--- a/internal/command/command.go
+++ b/internal/command/command.go
@@ -604,14 +604,11 @@ func RequireSession(ctx context.Context) (context.Context, error) {
 }
 
 func tryOpenUserURL(ctx context.Context, url string) error {
-	io := iostreams.FromContext(ctx)
-
-	if !io.IsInteractive() || env.IsCI() {
-		return errors.New("failed opening browser")
-	}
-
 	if err := open.Run(url); err != nil {
-		fmt.Fprintf(io.ErrOut, "failed opening browser. Copy the url (%s) into a browser and continue\n", url)
+		fmt.Fprintf(iostreams.FromContext(ctx).ErrOut,
+			"failed opening browser. Copy the url (%s) into a browser and continue\n",
+			url,
+		)
 	}
 
 	return nil
diff --git a/internal/command/command_run.go b/internal/command/command_run.go
index 71d4a7cbb..267e1d88c 100644
--- a/internal/command/command_run.go
+++ b/internal/command/command_run.go
@@ -73,15 +73,6 @@ func DetermineImage(ctx context.Context, appName string, imageOrPath string) (im
 		}
 		opts.BuildArgs = extraArgs
 
-		if cfg != nil && cfg.Experimental != nil {
-			opts.UseZstd = cfg.Experimental.UseZstd
-		}
-
-		// use-zstd passed through flags takes precedence over the one set in config
-		if flag.IsSpecified(ctx, "use-zstd") {
-			opts.UseZstd = flag.GetBool(ctx, "use-zstd")
-		}
-
 		img, err = resolver.BuildImage(ctx, io, opts)
 		if err != nil {
 			return nil, err
diff --git a/internal/command/console/console.go b/internal/command/console/console.go
index c26cb91c2..3d9b3b8ad 100644
--- a/internal/command/console/console.go
+++ b/internal/command/console/console.go
@@ -6,14 +6,12 @@ import (
 	"fmt"
 	"maps"
 
-	"github.com/docker/go-units"
 	"github.com/google/shlex"
 	"github.com/samber/lo"
 	"github.com/spf13/cobra"
 
 	fly "github.com/superfly/fly-go"
 	"github.com/superfly/fly-go/flaps"
-	"github.com/superfly/flyctl/helpers"
 	"github.com/superfly/flyctl/internal/appconfig"
 	"github.com/superfly/flyctl/internal/cmdutil"
 	"github.com/superfly/flyctl/internal/command"
@@ -404,15 +402,6 @@ func determineEphemeralConsoleMachineGuest(ctx context.Context, appConfig *appco
 
 	if compute := appConfig.ComputeForGroup("console"); compute != nil {
 		guest = compute.MachineGuest
-
-		if compute.Memory != "" {
-			mb, err := helpers.ParseSize(compute.Memory, units.RAMInBytes, units.MiB)
-			if err != nil {
-				return nil, fmt.Errorf("invalid memory size: %w", err)
-			}
-
-			guest.MemoryMB = mb
-		}
 	}
 
 	guest, err := flag.GetMachineGuest(ctx, guest)
@@ -429,7 +418,7 @@ func determineEphemeralConsoleMachineGuest(ctx context.Context, appConfig *appco
 		minMemory = guest.CPUs * fly.MIN_MEMORY_MB_PER_CPU
 		maxMemory = guest.CPUs * fly.MAX_MEMORY_MB_PER_CPU
 	default:
-		return nil, fmt.Errorf("invalid CPU kind '%s'; valid values are 'shared' and 'performance'", guest.CPUKind)
+		return nil, fmt.Errorf("invalid CPU kind '%s'; this is a bug", guest.CPUKind)
 	}
 
 	adjusted := lo.Clamp(guest.MemoryMB, minMemory, maxMemory)
diff --git a/internal/command/deploy/deploy_build.go b/internal/command/deploy/deploy_build.go
index dd0c50167..b1ec88337 100644
--- a/internal/command/deploy/deploy_build.go
+++ b/internal/command/deploy/deploy_build.go
@@ -134,8 +134,6 @@ func determineImage(ctx context.Context, appConfig *appconfig.Config, useWG, rec
 
 	if appConfig.Experimental != nil {
 		opts.UseOverlaybd = appConfig.Experimental.LazyLoadImages
-
-		opts.UseZstd = appConfig.Experimental.UseZstd
 	}
 
 	// flyctl supports key=value form while Docker supports id=key,src=/path/to/secret form.
diff --git a/internal/command/deploy/machines.go b/internal/command/deploy/machines.go
index 09bccc5bd..8dca79ee7 100644
--- a/internal/command/deploy/machines.go
+++ b/internal/command/deploy/machines.go
@@ -113,16 +113,13 @@ func argsFromManifest(manifest *DeployManifest, app *fly.AppCompact) MachineDepl
 }
 
 type machineDeployment struct {
-	// apiClient is a client to use web.
-	apiClient webClient
-	// flapsClient is a client to use flaps.
-	flapsClient flapsutil.FlapsClient
-	io          *iostreams.IOStreams
-	colorize    *iostreams.ColorScheme
-	app         *fly.AppCompact
-	appConfig   *appconfig.Config
-	img         string
-	// machineSet is this application's machines.
+	apiClient             flyutil.Client
+	flapsClient           flapsutil.FlapsClient
+	io                    *iostreams.IOStreams
+	colorize              *iostreams.ColorScheme
+	app                   *fly.AppCompact
+	appConfig             *appconfig.Config
+	img                   string
 	machineSet            machine.MachineSet
 	releaseCommandMachine machine.MachineSet
 	volumes               map[string][]fly.Volume
diff --git a/internal/command/deploy/machines_deploymachinesapp.go b/internal/command/deploy/machines_deploymachinesapp.go
index 0dc1da233..51261f77b 100644
--- a/internal/command/deploy/machines_deploymachinesapp.go
+++ b/internal/command/deploy/machines_deploymachinesapp.go
@@ -249,8 +249,6 @@ func (md *machineDeployment) inferCanaryGuest(processGroup string) *fly.MachineG
 	return canaryGuest
 }
 
-// deployCanaryMachines creates canary machines for each process group.
-// The canary machines are destroyed before returning to the caller.
 func (md *machineDeployment) deployCanaryMachines(ctx context.Context) (err error) {
 	ctx, span := tracing.GetTracer().Start(ctx, "deploy_canary")
 	defer span.End()
@@ -593,7 +591,6 @@ func (md *machineDeployment) updateExistingMachines(ctx context.Context, updateE
 	return err
 }
 
-// updateExistingMachinesWRecovery updates existing machines.
 // The code duplication is on purpose here. The plan is to completely move over to updateExistingMachinesWRecovery
 func (md *machineDeployment) updateExistingMachinesWRecovery(ctx context.Context, updateEntries []*machineUpdateEntry) (err error) {
 	ctx, span := tracing.GetTracer().Start(ctx, "update_existing_machines_w_recovery", trace.WithAttributes(
@@ -655,12 +652,9 @@ func (md *machineDeployment) updateExistingMachinesWRecovery(ctx context.Context
 		canaryAppState.Machines = []*fly.Machine{oldAppState.Machines[0]}
 
 		newCanaryAppState := newAppState
-		canaryMach, exists := lo.Find(newAppState.Machines, func(m *fly.Machine) bool {
+		canaryMach, _ := lo.Find(newAppState.Machines, func(m *fly.Machine) bool {
 			return m.ID == oldAppState.Machines[0].ID
 		})
-		if !exists {
-			return fmt.Errorf("failed to find machine %s under app %s", oldAppState.Machines[0].ID, md.app.Name)
-		}
 		newCanaryAppState.Machines = []*fly.Machine{canaryMach}
 
 		if err := md.updateMachinesWRecovery(ctx, &canaryAppState, &newCanaryAppState, nil, updateMachineSettings{
@@ -809,29 +803,32 @@ func (md *machineDeployment) updateUsingRollingStrategy(parentCtx context.Contex
 		groupsPool.Go(func(ctx context.Context) error {
 			eg, ctx := errgroup.WithContext(ctx)
 
-			coldIdx := startIdx
-			if len(coldMachines) > 0 {
-				eg.Go(func() error {
-					// Capping the size just in case, it may be okay to stop all of them at once.
-					chunk := len(coldMachines)
-					if chunk >= STOPPED_MACHINES_POOL_SIZE {
-						chunk = STOPPED_MACHINES_POOL_SIZE
-					}
-					return md.updateEntriesGroup(ctx, group, coldMachines, sl, coldIdx, chunk)
-				})
-			}
-			startIdx += len(coldMachines)
-
-			warmIdx := startIdx
-			if len(warmMachines) > 0 {
-				eg.Go(func() error {
-					// Since these machines are still receiving traffic, the chunk size here is more conservative (lower)
-					// then the one above.
-					chunk := md.getPoolSize(len(warmMachines))
-					return md.updateEntriesGroup(ctx, group, warmMachines, sl, warmIdx, chunk)
-				})
-			}
-			startIdx += len(warmMachines)
+			eg.Go(func() (err error) {
+				poolSize := len(coldMachines)
+				if poolSize >= STOPPED_MACHINES_POOL_SIZE {
+					poolSize = STOPPED_MACHINES_POOL_SIZE
+				}
+
+				if len(coldMachines) > 0 {
+					// for cold machines, we can update all of them at once.
+					// there's no need for protection against downtime since the machines are already stopped
+					startIdx += len(coldMachines)
+					return md.updateEntriesGroup(ctx, group, coldMachines, sl, startIdx-len(coldMachines), poolSize)
+				}
+
+				return nil
+			})
+
+			eg.Go(func() (err error) {
+				// for warm machines, we update them in chunks of size, md.maxUnavailable.
+				// this is to prevent downtime/low-latency during deployments
+				startIdx += len(warmMachines)
+				poolSize := md.getPoolSize(len(warmMachines))
+				if len(warmMachines) > 0 {
+					return md.updateEntriesGroup(ctx, group, warmMachines, sl, startIdx-len(warmMachines), poolSize)
+				}
+				return nil
+			})
 
 			return eg.Wait()
 		})
@@ -1103,7 +1100,6 @@ func (md *machineDeployment) spawnMachineInGroup(ctx context.Context, groupName
 	return lm, nil
 }
 
-// resolveProcessGroupChanges returns a diff between machines
 func (md *machineDeployment) resolveProcessGroupChanges() ProcessGroupsDiff {
 	output := ProcessGroupsDiff{
 		groupsToRemove:        map[string]int{},
diff --git a/internal/command/deploy/machines_releasecommand.go b/internal/command/deploy/machines_releasecommand.go
index 94a360681..1448c4204 100644
--- a/internal/command/deploy/machines_releasecommand.go
+++ b/internal/command/deploy/machines_releasecommand.go
@@ -1,7 +1,6 @@
 package deploy
 
 import (
-	"container/ring"
 	"context"
 	"errors"
 	"fmt"
@@ -10,21 +9,16 @@ import (
 	"strings"
 	"time"
 
-	"github.com/logrusorgru/aurora"
 	"github.com/samber/lo"
 	fly "github.com/superfly/fly-go"
 	"github.com/superfly/fly-go/flaps"
 	"github.com/superfly/flyctl/helpers"
 	"github.com/superfly/flyctl/internal/appconfig"
-	"github.com/superfly/flyctl/internal/config"
 	"github.com/superfly/flyctl/internal/flag"
-	"github.com/superfly/flyctl/internal/format"
 	"github.com/superfly/flyctl/internal/machine"
 	"github.com/superfly/flyctl/internal/statuslogger"
 	"github.com/superfly/flyctl/internal/tracing"
-	"github.com/superfly/flyctl/logs"
 	"go.opentelemetry.io/otel/trace"
-	"golang.org/x/sync/errgroup"
 )
 
 func (md *machineDeployment) runReleaseCommand(ctx context.Context) (err error) {
@@ -53,74 +47,12 @@ func (md *machineDeployment) runReleaseCommand(ctx context.Context) (err error)
 		loggerCleanup(false)
 	}()
 
-	logOpts := &logs.LogOptions{
-		AppName:    appconfig.NameFromContext(ctx),
-		RegionCode: config.FromContext(ctx).Region,
-		NoTail:     false,
-	}
-	var stream logs.LogStream
-
-	eg, groupCtx := errgroup.WithContext(ctx)
-	eg.Go(func() error {
-		err := md.createOrUpdateReleaseCmdMachine(groupCtx)
-		if err != nil {
-			tracing.RecordError(span, err, "failed to create release cmd machine")
-			return fmt.Errorf("error running release_command machine: %w", err)
-		}
-		return nil
-	})
-	eg.Go(func() error {
-		stream, err = logs.NewNatsStream(ctx, md.apiClient, logOpts)
-		if err != nil {
-			// Silently fallback to app logs polling if NATS streaming client is unavailable.
-			stream = logs.NewPollingStream(md.apiClient)
-		}
-		return nil
-	})
-	if err = eg.Wait(); err != nil {
-		return err
+	err = md.createOrUpdateReleaseCmdMachine(ctx)
+	if err != nil {
+		tracing.RecordError(span, err, "failed to create release cmd machine")
+		return fmt.Errorf("error running release_command machine: %w", err)
 	}
-
 	releaseCmdMachine := md.releaseCommandMachine.GetMachines()[0]
-
-	logOpts.VMID = releaseCmdMachine.Machine().ID
-	logsCtx, cancelLogs := context.WithCancel(ctx)
-	defer cancelLogs()
-	var buf *ring.Ring
-	if !flag.GetBool(ctx, "verbose") {
-		buf = ring.New(100)
-	}
-	go func() {
-		defer cancelLogs()
-		if stream == nil {
-			return
-		}
-		for entry := range stream.Stream(logsCtx, logOpts) {
-			var ts time.Time
-			if ts, err = time.Parse(time.RFC3339Nano, entry.Timestamp); err != nil {
-				err = fmt.Errorf("failed parsing timestamp %q: %w", entry.Timestamp, err)
-				return
-			}
-			msg := fmt.Sprintf("%s %s", aurora.Faint(format.Time(ts)), entry.Message)
-			if buf != nil {
-				buf.Value = msg
-				buf = buf.Next()
-			} else {
-				fmt.Fprintln(md.io.ErrOut)
-			}
-			if strings.Contains(entry.Message, "Main child exited normally") {
-				return
-			}
-		}
-	}()
-
-	fmt.Fprintln(md.io.ErrOut, "Starting machine")
-
-	if err = releaseCmdMachine.Start(ctx); err != nil {
-		fmt.Fprintf(md.io.ErrOut, "error starting release_command machine: %v\n", err)
-		return
-	}
-
 	// FIXME: consolidate this wait stuff with deploy waits? Especially once we improve the outpu
 	err = md.waitForReleaseCommandToFinish(ctx, releaseCmdMachine)
 	if err != nil {
@@ -137,29 +69,35 @@ func (md *machineDeployment) runReleaseCommand(ctx context.Context) (err error)
 		return fmt.Errorf("error get release_command machine %s exit code: %w", releaseCmdMachine.Machine().ID, err)
 	}
 
-	if flag.GetBool(ctx, "verbose") {
-		waitForLogs(md, logsCtx, stream, releaseCmdMachine.Machine().ID)
-	}
-
-	if exitCode != 0 {
-		statuslogger.LogStatus(ctx, statuslogger.StatusFailure, "release_command failed")
+	if exitCode != 0 || flag.GetBool(ctx, "verbose") {
+		if exitCode != 0 {
+			statuslogger.LogStatus(ctx, statuslogger.StatusFailure, "release_command failed")
+		}
 
 		// Preemptive cleanup of the logger so that the logs have a clean place to write to
 		loggerCleanup(false)
 
-		fmt.Fprintf(md.io.ErrOut, "Error release_command failed running on machine %s with exit code %s.\n",
-			md.colorize.Bold(releaseCmdMachine.Machine().ID), md.colorize.Red(strconv.Itoa(exitCode)))
-
-		if !flag.GetBool(ctx, "verbose") {
-			fmt.Fprintf(md.io.ErrOut, "Checking logs: fetching the last 100 lines below:\n")
-			waitForLogs(md, logsCtx, stream, releaseCmdMachine.Machine().ID)
-			buf.Do(func(str any) {
-				if str != nil {
-					fmt.Fprintln(md.io.ErrOut, str)
-				}
-			})
+		time.Sleep(2 * time.Second) // Wait 2 secs to be sure logs have reached OpenSearch
+		if exitCode != 0 {
+			fmt.Fprintf(md.io.ErrOut, "Error release_command failed running on machine %s with exit code %s.\n",
+				md.colorize.Bold(releaseCmdMachine.Machine().ID), md.colorize.Red(strconv.Itoa(exitCode)))
+		}
+		fmt.Fprintf(md.io.ErrOut, "Check its logs: here's the last 100 lines below, or run 'fly logs -i %s':\n",
+			releaseCmdMachine.Machine().ID)
+		releaseCmdLogs, _, err := md.apiClient.GetAppLogs(ctx, md.app.Name, "", md.appConfig.PrimaryRegion, releaseCmdMachine.Machine().ID)
+		if fly.IsNotAuthenticatedError(err) {
+			fmt.Fprintf(md.io.ErrOut, "Warn: not authorized to retrieve app logs (this can happen when using deploy tokens), so we can't show you what failed. Use `fly logs -i %s` or open the monitoring dashboard to see them: https://fly.io/apps/%s/monitoring?region=&instance=%s\n", releaseCmdMachine.Machine().ID, md.appConfig.AppName, releaseCmdMachine.Machine().ID)
+		} else {
+			if err != nil {
+				return fmt.Errorf("error getting release_command logs: %w", err)
+			}
+			for _, l := range releaseCmdLogs {
+				fmt.Fprintf(md.io.ErrOut, "  %s\n", l.Message)
+			}
+		}
+		if exitCode != 0 {
+			return fmt.Errorf("error release_command machine %s exited with non-zero status of %d", releaseCmdMachine.Machine().ID, exitCode)
 		}
-		return fmt.Errorf("machine %s exited with non-zero status of %d", releaseCmdMachine.Machine().ID, exitCode)
 	}
 	statuslogger.LogfStatus(ctx,
 		statuslogger.StatusSuccess,
@@ -169,22 +107,6 @@ func (md *machineDeployment) runReleaseCommand(ctx context.Context) (err error)
 	return nil
 }
 
-// Wait up to 20 secs to be sure logs have been fully ingested, and log any errors.
-func waitForLogs(md *machineDeployment, ctx context.Context, stream logs.LogStream, id string) {
-	timer := time.NewTimer(20 * time.Second)
-	defer timer.Stop()
-	select {
-	case <-ctx.Done():
-		if fly.IsNotAuthenticatedError(stream.Err()) {
-			fmt.Fprintf(md.io.ErrOut, "Warn: not authorized to retrieve app logs (this can happen when using deploy tokens). Use `fly logs -i %s` or open the monitoring dashboard to see them: https://fly.io/apps/%s/monitoring?region=&instance=%s\n", id, md.appConfig.AppName, id)
-		} else if stream.Err() != nil && !errors.Is(stream.Err(), context.Canceled) {
-			fmt.Fprintf(md.io.ErrOut, "error getting release command logs: %v\n", stream.Err())
-		}
-	case <-timer.C:
-		fmt.Fprintf(md.io.ErrOut, "timeout waiting for release command logs\n")
-	}
-}
-
 // dedicatedHostIdMismatch checks if the dedicatedHostID on a machine is the same as the one set in the fly.toml
 // a mismatch will result in a delete+recreate op
 func dedicatedHostIdMismatch(m *fly.Machine, ac *appconfig.Config) bool {
@@ -225,13 +147,6 @@ func (md *machineDeployment) createReleaseCommandMachine(ctx context.Context) er
 
 	statuslogger.Logf(ctx, "Created release_command machine %s", md.colorize.Bold(releaseCmdMachine.ID))
 	md.releaseCommandMachine = machine.NewMachineSet(md.flapsClient, md.io, []*fly.Machine{releaseCmdMachine}, true)
-
-	lm := md.releaseCommandMachine.GetMachines()[0]
-	if err := lm.WaitForState(ctx, fly.MachineStateStopped, md.waitTimeout, false); err != nil {
-		err = suggestChangeWaitTimeout(err, "wait-timeout")
-		return err
-	}
-
 	return nil
 }
 
@@ -270,8 +185,10 @@ func (md *machineDeployment) launchInputForReleaseCommand(origMachineRaw *fly.Ma
 	// We can ignore the error because ToReleaseMachineConfig fails only
 	// if it can't split the command and we test that at initialization
 	mConfig, _ := md.appConfig.ToReleaseMachineConfig()
-	mConfig.Guest = md.inferReleaseCommandGuest()
 	mConfig.Image = md.img
+	if mConfig.Guest == nil {
+		mConfig.Guest = md.inferReleaseCommandGuest()
+	}
 	md.setMachineReleaseData(mConfig)
 
 	if hdid := md.appConfig.HostDedicationID; hdid != "" {
@@ -279,9 +196,8 @@ func (md *machineDeployment) launchInputForReleaseCommand(origMachineRaw *fly.Ma
 	}
 
 	return &fly.LaunchMachineInput{
-		Config:     mConfig,
-		Region:     origMachineRaw.Region,
-		SkipLaunch: true,
+		Config: mConfig,
+		Region: origMachineRaw.Region,
 	}
 }
 
diff --git a/internal/command/deploy/plan.go b/internal/command/deploy/plan.go
index b22e2c904..62a18f13d 100644
--- a/internal/command/deploy/plan.go
+++ b/internal/command/deploy/plan.go
@@ -60,7 +60,6 @@ type machinePairing struct {
 	newMachine *fly.Machine
 }
 
-// appState returns the app's state from Flaps.
 func (md *machineDeployment) appState(ctx context.Context, existingAppState *AppState) (*AppState, error) {
 	ctx, span := tracing.GetTracer().Start(ctx, "app_state")
 	defer span.End()
@@ -369,9 +368,6 @@ func (md *machineDeployment) acquireLeases(ctx context.Context, machineTuples []
 	ctx, span := tracing.GetTracer().Start(ctx, "acquire_leases")
 
 	leaseGroup := errgroup.Group{}
-	if poolSize <= 0 {
-		panic("pool size must be > 0")
-	}
 	leaseGroup.SetLimit(poolSize)
 
 	for _, machineTuple := range machineTuples {
diff --git a/internal/command/deploy/web_client.go b/internal/command/deploy/web_client.go
deleted file mode 100644
index b014301b1..000000000
--- a/internal/command/deploy/web_client.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package deploy
-
-import (
-	"context"
-	"net"
-
-	fly "github.com/superfly/fly-go"
-	"github.com/superfly/flyctl/logs"
-)
-
-// webClient is a subset of web API that is needed for the deploy package.
-type webClient interface {
-	AddCertificate(ctx context.Context, appName, hostname string) (*fly.AppCertificate, *fly.HostnameCheck, error)
-	AllocateIPAddress(ctx context.Context, appName string, addrType string, region string, org *fly.Organization, network string) (*fly.IPAddress, error)
-	GetIPAddresses(ctx context.Context, appName string) ([]fly.IPAddress, error)
-	AllocateSharedIPAddress(ctx context.Context, appName string) (net.IP, error)
-
-	LatestImage(ctx context.Context, appName string) (string, error)
-
-	CreateRelease(ctx context.Context, input fly.CreateReleaseInput) (*fly.CreateReleaseResponse, error)
-	UpdateRelease(ctx context.Context, input fly.UpdateReleaseInput) (*fly.UpdateReleaseResponse, error)
-
-	GetApp(ctx context.Context, appName string) (*fly.App, error)
-	GetOrganizationBySlug(ctx context.Context, slug string) (*fly.Organization, error)
-
-	logs.WebClient
-	blueGreenWebClient
-}
diff --git a/internal/command/machine/run.go b/internal/command/machine/run.go
index e0a589cc9..2c28b988c 100644
--- a/internal/command/machine/run.go
+++ b/internal/command/machine/run.go
@@ -2,11 +2,8 @@ package machine
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
-	"io"
 	"math/rand"
-	"os"
 	"strconv"
 	"strings"
 	"time"
@@ -94,10 +91,6 @@ var sharedFlags = flag.Set{
 		Description: "Do not use the cache when building the image",
 		Hidden:      true,
 	},
-	flag.String{
-		Name:        "machine-config",
-		Description: "Read machine config from json file or string",
-	},
 	flag.StringArray{
 		Name:        "kernel-arg",
 		Description: "A list of kernel arguments to provide to the init. Can be specified multiple times.",
@@ -180,10 +173,6 @@ var runOrCreateFlags = flag.Set{
 		Description: "Enable LSVD for this machine",
 		Hidden:      true,
 	},
-	flag.Bool{
-		Name:        "use-zstd",
-		Description: "Enable zstd compression for the image",
-	},
 }
 
 func soManyErrors(args ...interface{}) error {
@@ -647,29 +636,6 @@ func determineMachineConfig(
 ) (*fly.MachineConfig, error) {
 	machineConf := mach.CloneConfig(&input.initialMachineConf)
 
-	if emc := flag.GetString(ctx, "machine-config"); emc != "" {
-		var buf []byte
-		switch {
-		case strings.HasPrefix(emc, "{"):
-			buf = []byte(emc)
-		case strings.HasSuffix(emc, ".json"):
-			fo, err := os.Open(emc)
-			if err != nil {
-				return nil, err
-			}
-			buf, err = io.ReadAll(fo)
-			if err != nil {
-				return nil, err
-			}
-		default:
-			return nil, fmt.Errorf("invalid machine config source: %q", emc)
-		}
-
-		if err := json.Unmarshal(buf, machineConf); err != nil {
-			return nil, fmt.Errorf("invalid machine config %q: %w", emc, err)
-		}
-	}
-
 	var err error
 	machineConf.Guest, err = flag.GetMachineGuest(ctx, machineConf.Guest)
 	if err != nil {
diff --git a/internal/command/registry/command.go b/internal/command/registry/command.go
index 7365614bf..99cb2d214 100644
--- a/internal/command/registry/command.go
+++ b/internal/command/registry/command.go
@@ -17,7 +17,6 @@ func New() *cobra.Command {
 	cmd.Hidden = true
 
 	cmd.AddCommand(
-		newFiles(),
 		newSbom(),
 		newVulns(),
 		newVulnSummary(),
diff --git a/internal/command/registry/files.go b/internal/command/registry/files.go
deleted file mode 100644
index dd3db98dc..000000000
--- a/internal/command/registry/files.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package registry
-
-import (
-	"context"
-	"fmt"
-	"io"
-	"net/http"
-
-	"github.com/spf13/cobra"
-
-	"github.com/superfly/flyctl/internal/command"
-	"github.com/superfly/flyctl/internal/flag"
-	"github.com/superfly/flyctl/iostreams"
-)
-
-func newFiles() *cobra.Command {
-	const (
-		usage = "files"
-		short = "Generate a file listing for a registry image [experimental]"
-		long  = "Genearte a file listing for a registry iamge.\n" +
-			"The image is selected by name, or the image of the app's first machine\n" +
-			"is used unless interactive machine selection or machine ID is specified."
-	)
-	cmd := command.New(usage, short, long, runFiles,
-		command.RequireSession,
-		command.RequireAppName,
-	)
-
-	cmd.Args = cobra.NoArgs
-	flag.Add(
-		cmd,
-		flag.App(),
-		flag.String{
-			Name:        "image",
-			Shorthand:   "i",
-			Description: "Scan the repository image",
-		},
-		flag.String{
-			Name:        "machine",
-			Description: "Scan the image of the machine with the specified ID",
-		},
-		flag.Bool{
-			Name:        "select",
-			Shorthand:   "s",
-			Description: "Select which machine to scan the image of from a list.",
-			Default:     false,
-		},
-	)
-
-	return cmd
-}
-
-func runFiles(ctx context.Context) error {
-	imgPath, orgId, err := argsGetImgPath(ctx)
-	if err != nil {
-		return err
-	}
-
-	token, err := makeScantronToken(ctx, orgId)
-	if err != nil {
-		return err
-	}
-
-	res, err := scantronFilesReq(ctx, imgPath, token)
-	if err != nil {
-		return err
-	}
-	defer res.Body.Close() // skipcq: GO-S2307
-
-	if res.StatusCode != http.StatusOK {
-		return fmt.Errorf("failed fetching file listing (status code %d)", res.StatusCode)
-	}
-
-	ios := iostreams.FromContext(ctx)
-	if _, err := io.Copy(ios.Out, res.Body); err != nil {
-		return fmt.Errorf("failed to read file listing: %w", err)
-	}
-	return nil
-}
diff --git a/internal/command/registry/sbom.go b/internal/command/registry/sbom.go
index ce5db3f9b..d8db7bdc0 100644
--- a/internal/command/registry/sbom.go
+++ b/internal/command/registry/sbom.go
@@ -16,7 +16,7 @@ import (
 func newSbom() *cobra.Command {
 	const (
 		usage = "sbom"
-		short = "Generate an SBOM for a registry image [experimental]"
+		short = "Generate an SBOM for a registry iamge [experimental]"
 		long  = "Genearte an SBOM for a registry image.\n" +
 			"The image is selected by name, or the image of the app's first machine\n" +
 			"is used unless interactive machine selection or machine ID is specified."
diff --git a/internal/command/registry/scantron.go b/internal/command/registry/scantron.go
index 70e4de101..f10847b2a 100644
--- a/internal/command/registry/scantron.go
+++ b/internal/command/registry/scantron.go
@@ -62,10 +62,6 @@ func scantronVulnscanReq(ctx context.Context, imgPath, token string) (*http.Resp
 	return scantronReq(ctx, imgPath, token, "application/json")
 }
 
-func scantronFilesReq(ctx context.Context, imgPath, token string) (*http.Response, error) {
-	return scantronReq(ctx, imgPath+"?files=1", token, "application/json")
-}
-
 type Scan struct {
 	SchemaVersion int
 	CreatedAt     string
diff --git a/internal/command/scale/count_machines.go b/internal/command/scale/count_machines.go
index 51aa6d1e6..8fab9cd8a 100644
--- a/internal/command/scale/count_machines.go
+++ b/internal/command/scale/count_machines.go
@@ -20,8 +20,6 @@ import (
 	"github.com/superfly/flyctl/iostreams"
 )
 
-const maxConcurrentActions = 5
-
 func runMachinesScaleCount(ctx context.Context, appName string, appConfig *appconfig.Config, expectedGroupCounts groupCounts, maxPerRegion int) error {
 	io := iostreams.FromContext(ctx)
 	flapsClient := flapsutil.ClientFromContext(ctx)
@@ -133,6 +131,16 @@ func runMachinesScaleCount(ctx context.Context, appName string, appConfig *appco
 		return err
 	}
 
+	// Deleting machines is safe to parallelize,
+	// but creating machines is not because of how the platform propagetes data.
+	maxConcurrentActions := 5
+	_, scaleUp := lo.Find(actions, func(a *planItem) bool {
+		return a.Delta > 0
+	})
+	if scaleUp {
+		maxConcurrentActions = 1
+	}
+
 	updatePool := pool.New().
 		WithErrors().
 		WithMaxGoroutines(maxConcurrentActions).
diff --git a/internal/command/ssh/sftp.go b/internal/command/ssh/sftp.go
index 226e1ac5e..04ec9fa1e 100644
--- a/internal/command/ssh/sftp.go
+++ b/internal/command/ssh/sftp.go
@@ -95,12 +95,7 @@ func newSFTPConnection(ctx context.Context) (*sftp.Client, error) {
 		return nil, fmt.Errorf("get app: %w", err)
 	}
 
-	network, err := client.GetAppNetwork(ctx, appName)
-	if err != nil {
-		return nil, fmt.Errorf("get app network: %w", err)
-	}
-
-	agentclient, dialer, err := BringUpAgent(ctx, client, app, *network, quiet(ctx))
+	agentclient, dialer, err := BringUpAgent(ctx, client, app, "", quiet(ctx))
 	if err != nil {
 		return nil, err
 	}
diff --git a/internal/config/tokens.go b/internal/config/tokens.go
index 6d6749968..84e4b5f9d 100644
--- a/internal/config/tokens.go
+++ b/internal/config/tokens.go
@@ -47,7 +47,7 @@ func MonitorTokens(monitorCtx context.Context, t *tokens.Tokens, uucb UserURLCal
 		log.Debugf("failed to update discharge tokens: %s", err)
 	}
 
-	if file != "" && (updated1 || updated2) {
+	if file != "" && updated1 || updated2 {
 		if err := SetAccessToken(file, t.All()); err != nil {
 			log.Debugf("failed to persist updated tokens: %s", err)
 		}
@@ -198,11 +198,6 @@ func refreshDischargeTokens(ctx context.Context, t *tokens.Tokens, uucb UserURLC
 //
 // Don't call this when other goroutines might also be accessing t.
 func fetchOrgTokens(ctx context.Context, t *tokens.Tokens) (bool, error) {
-	// don't fetch missing org tokens if tokens came from environment var
-	if t.FromFile() == "" {
-		return false, nil
-	}
-
 	return doFetchOrgTokens(ctx, t, defaultOrgFetcher, defaultTokenMinter)
 }
 
diff --git a/internal/machine/leasable_machine.go b/internal/machine/leasable_machine.go
index 565351690..cdab8c815 100644
--- a/internal/machine/leasable_machine.go
+++ b/internal/machine/leasable_machine.go
@@ -5,7 +5,6 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
-	"sync"
 	"time"
 
 	"github.com/jpillora/backoff"
@@ -50,14 +49,10 @@ type leasableMachine struct {
 	io                     *iostreams.IOStreams
 	colorize               *iostreams.ColorScheme
 	machine                *fly.Machine
+	leaseNonce             string
 	leaseRefreshCancelFunc context.CancelFunc
 	destroyed              bool
 	showLogs               bool
-
-	// mu protects leaseNonce. A leasableMachine shouldn't be shared between
-	// goroutines, but StartBackgroundLeaseRefresh breaks the rule.
-	mu         sync.Mutex
-	leaseNonce string
 }
 
 // TODO: make sure the other functions handle showLogs correctly
@@ -471,9 +466,6 @@ func (lm *leasableMachine) AcquireLease(ctx context.Context, duration time.Durat
 }
 
 func (lm *leasableMachine) RefreshLease(ctx context.Context, duration time.Duration) error {
-	lm.mu.Lock()
-	defer lm.mu.Unlock()
-
 	seconds := int(duration.Seconds())
 	refreshedLease, err := lm.flapsClient.RefreshLease(ctx, lm.machine.ID, &seconds, lm.leaseNonce)
 	if err != nil {
@@ -517,9 +509,6 @@ func (lm *leasableMachine) refreshLeaseUntilCanceled(ctx context.Context, durati
 }
 
 func (lm *leasableMachine) ReleaseLease(ctx context.Context) error {
-	lm.mu.Lock()
-	defer lm.mu.Unlock()
-
 	nonce := lm.leaseNonce
 	lm.resetLease()
 	if nonce == "" {
diff --git a/internal/statuslogger/noninteractive.go b/internal/statuslogger/noninteractive.go
index f3cb14360..434da7511 100644
--- a/internal/statuslogger/noninteractive.go
+++ b/internal/statuslogger/noninteractive.go
@@ -3,19 +3,15 @@ package statuslogger
 import (
 	"fmt"
 	"strings"
-	"sync"
 
 	"github.com/superfly/flyctl/iostreams"
 )
 
 type noninteractiveLogger struct {
-	// mu protects io.
-	mu sync.Mutex
-	io *iostreams.IOStreams
-
+	io         *iostreams.IOStreams
+	lines      []*noninteractiveLine
 	logNumbers bool
 	showStatus bool
-	lines      []*noninteractiveLine
 }
 
 func (nl *noninteractiveLogger) Line(i int) StatusLine {
@@ -40,14 +36,7 @@ func (line *noninteractiveLine) Log(s string) {
 		buf += formatIndex(line.lineNum, len(line.logger.lines)) + " "
 	}
 	buf += s
-
-	line.println(buf)
-}
-
-func (line *noninteractiveLine) println(s string) {
-	line.logger.mu.Lock()
-	defer line.logger.mu.Unlock()
-	fmt.Fprintln(line.logger.io.Out, s)
+	fmt.Fprintln(line.logger.io.Out, buf)
 }
 
 func (line *noninteractiveLine) Logf(format string, args ...interface{}) {
diff --git a/internal/wireguard/wg.go b/internal/wireguard/wg.go
index 54acfcde7..a465e517b 100644
--- a/internal/wireguard/wg.go
+++ b/internal/wireguard/wg.go
@@ -24,10 +24,6 @@ import (
 
 var cleanDNSPattern = regexp.MustCompile(`[^a-zA-Z0-9\\-]`)
 
-type WebClient interface {
-	ValidateWireGuardPeers(ctx context.Context, peerIPs []string) (invalid []string, err error)
-}
-
 func generatePeerName(ctx context.Context, apiClient flyutil.Client) (string, error) {
 	user, err := apiClient.GetCurrentUser(ctx)
 	if err != nil {
@@ -185,7 +181,7 @@ func setWireGuardStateForOrg(ctx context.Context, orgSlug, network string, s *wg
 	return setWireGuardState(ctx, states)
 }
 
-func PruneInvalidPeers(ctx context.Context, apiClient WebClient) error {
+func PruneInvalidPeers(ctx context.Context, apiClient flyutil.Client) error {
 	state, err := GetWireGuardState()
 	if err != nil {
 		return nil
diff --git a/logs/logs.go b/logs/logs.go
index e1d31f05e..61e7e96af 100644
--- a/logs/logs.go
+++ b/logs/logs.go
@@ -5,9 +5,6 @@ import (
 	"fmt"
 	"io"
 	"time"
-
-	fly "github.com/superfly/fly-go"
-	"github.com/superfly/flyctl/internal/wireguard"
 )
 
 type LogOptions struct {
@@ -20,12 +17,6 @@ type LogOptions struct {
 	NoTail     bool
 }
 
-type WebClient interface {
-	GetAppBasic(ctx context.Context, appName string) (*fly.AppBasic, error)
-	GetAppLogs(ctx context.Context, appName, token, region, instanceID string) (entries []fly.LogEntry, nextToken string, err error)
-	wireguard.WebClient
-}
-
 func (opts *LogOptions) toNatsSubject() (subject string) {
 	subject = fmt.Sprintf("logs.%s", opts.AppName)
 
diff --git a/logs/nats.go b/logs/nats.go
index 19ebc63d3..8eb68259c 100644
--- a/logs/nats.go
+++ b/logs/nats.go
@@ -10,6 +10,7 @@ import (
 
 	"github.com/superfly/flyctl/agent"
 	"github.com/superfly/flyctl/internal/config"
+	"github.com/superfly/flyctl/internal/flyutil"
 )
 
 type natsLogStream struct {
@@ -17,7 +18,7 @@ type natsLogStream struct {
 	err error
 }
 
-func NewNatsStream(ctx context.Context, apiClient WebClient, opts *LogOptions) (LogStream, error) {
+func NewNatsStream(ctx context.Context, apiClient flyutil.Client, opts *LogOptions) (LogStream, error) {
 	app, err := apiClient.GetAppBasic(ctx, opts.AppName)
 	if err != nil {
 		return nil, fmt.Errorf("failed fetching target app: %w", err)
diff --git a/logs/polling.go b/logs/polling.go
index f10b34155..e7c214f63 100644
--- a/logs/polling.go
+++ b/logs/polling.go
@@ -8,15 +8,16 @@ import (
 	"github.com/pkg/errors"
 
 	fly "github.com/superfly/fly-go"
+	"github.com/superfly/flyctl/internal/flyutil"
 )
 
 type pollingStream struct {
 	err       error
-	apiClient WebClient
+	apiClient flyutil.Client
 }
 
-func NewPollingStream(client WebClient) LogStream {
-	return &pollingStream{apiClient: client}
+func NewPollingStream(client flyutil.Client, opts *LogOptions) (LogStream, error) {
+	return &pollingStream{apiClient: client}, nil
 }
 
 func (s *pollingStream) Stream(ctx context.Context, opts *LogOptions) <-chan LogEntry {
@@ -35,7 +36,7 @@ func (s *pollingStream) Err() error {
 	return s.err
 }
 
-func Poll(ctx context.Context, out chan<- LogEntry, client WebClient, opts *LogOptions) error {
+func Poll(ctx context.Context, out chan<- LogEntry, client flyutil.Client, opts *LogOptions) error {
 	const (
 		minWait = time.Millisecond << 6
 		maxWait = minWait << 6


Test patch:
diff --git a/.github/workflows/test_install.yml b/.github/workflows/test_install.yml
index cc408f6cf..21718d133 100644
--- a/.github/workflows/test_install.yml
+++ b/.github/workflows/test_install.yml
@@ -70,11 +70,10 @@ jobs:
       max-parallel: 4
       matrix:
         os:
-          # From https://github.com/actions/runner-images/tree/main?tab=readme-ov-file#available-images.
-          - ubuntu-24.04
-          - ubuntu-22.04
+          - ubuntu-latest
           - ubuntu-20.04
-          - macos-14
+          - macos-latest
+          - macos-12
           - macos-13
     runs-on: ${{ matrix.os }}
     steps:
diff --git a/internal/appconfig/definition_test.go b/internal/appconfig/definition_test.go
index 7da99bb77..d2a8047af 100644
--- a/internal/appconfig/definition_test.go
+++ b/internal/appconfig/definition_test.go
@@ -257,9 +257,14 @@ func TestToDefinition(t *testing.T) {
 		},
 
 		"deploy": map[string]any{
-			"release_command": "release command",
-			"strategy":        "rolling-eyes",
-			"max_unavailable": 0.2,
+			"strategy":                "rolling-eyes",
+			"max_unavailable":         0.2,
+			"release_command":         "release command",
+			"release_command_timeout": "3m0s",
+			"release_command_vm": map[string]any{
+				"size":   "performance-2x",
+				"memory": "8g",
+			},
 		},
 		"env": map[string]any{
 			"FOO": "BAR",
diff --git a/internal/appconfig/machines_test.go b/internal/appconfig/machines_test.go
index e222bb92e..2d47da8b1 100644
--- a/internal/appconfig/machines_test.go
+++ b/internal/appconfig/machines_test.go
@@ -53,7 +53,6 @@ func TestToMachineConfig(t *testing.T) {
 		Restart: &fly.MachineRestart{
 			Policy: fly.MachineRestartPolicyAlways,
 		},
-		DNS: &fly.DNSConfig{Nameservers: []string{"1.2.3.4"}},
 	}
 
 	got, err := cfg.ToMachineConfig("", nil)
@@ -80,7 +79,7 @@ func TestToMachineConfig(t *testing.T) {
 	assert.Equal(t, "24/7", got.Schedule)
 	assert.Equal(t, true, got.AutoDestroy)
 	assert.Equal(t, &fly.MachineRestart{Policy: "always"}, got.Restart)
-	assert.Equal(t, &fly.DNSConfig{SkipRegistration: true, Nameservers: []string{"1.2.3.4"}}, got.DNS)
+	assert.Equal(t, &fly.DNSConfig{SkipRegistration: true}, got.DNS)
 	assert.Equal(t, "propagated", got.Metadata["retain"])
 	assert.Empty(t, got.Init.Cmd)
 }
@@ -169,6 +168,11 @@ func TestToReleaseMachineConfig(t *testing.T) {
 			Timeout: fly.MustParseDuration("10s"),
 			Signal:  fly.Pointer("SIGTERM"),
 		},
+		Guest: &fly.MachineGuest{
+			CPUKind:  "performance",
+			CPUs:     2,
+			MemoryMB: 4096,
+		},
 	}
 
 	got, err := cfg.ToReleaseMachineConfig()
@@ -461,7 +465,7 @@ func TestToTestMachineConfigWTestMachine(t *testing.T) {
 	}
 	got, err := cfg.ToTestMachineConfig(check, machine)
 	assert.NoError(t, err)
-	assert.Equal(t, want, got)
+	assert.Equal(t, got, want)
 }
 
 func TestToConsoleMachineConfig(t *testing.T) {
diff --git a/internal/appconfig/serde_test.go b/internal/appconfig/serde_test.go
index 25b2e71fc..79456c715 100644
--- a/internal/appconfig/serde_test.go
+++ b/internal/appconfig/serde_test.go
@@ -388,9 +388,14 @@ func TestLoadTOMLAppConfigReferenceFormat(t *testing.T) {
 		},
 
 		Deploy: &Deploy{
-			ReleaseCommand: "release command",
-			Strategy:       "rolling-eyes",
-			MaxUnavailable: fly.Pointer(0.2),
+			Strategy:              "rolling-eyes",
+			MaxUnavailable:        fly.Pointer(0.2),
+			ReleaseCommand:        "release command",
+			ReleaseCommandTimeout: fly.MustParseDuration("3m"),
+			ReleaseCommandCompute: &Compute{
+				Size:   "performance-2x",
+				Memory: "8g",
+			},
 		},
 
 		Env: map[string]string{
diff --git a/internal/appconfig/testdata/full-reference.toml b/internal/appconfig/testdata/full-reference.toml
index a1ce51d43..b81d6a086 100644
--- a/internal/appconfig/testdata/full-reference.toml
+++ b/internal/appconfig/testdata/full-reference.toml
@@ -34,6 +34,9 @@ host_dedication_id = "06031957"
 
 [deploy]
   release_command = "release command"
+  release_command_timeout = "3m"
+  release_command_vm.size = "performance-2x"
+  release_command_vm.memory = "8g"
   strategy = "rolling-eyes"
   max_unavailable = 0.2
 
diff --git a/internal/appconfig/testdata/tomachine.toml b/internal/appconfig/testdata/tomachine.toml
index 242eb67e5..fab746cbd 100644
--- a/internal/appconfig/testdata/tomachine.toml
+++ b/internal/appconfig/testdata/tomachine.toml
@@ -6,6 +6,10 @@ swap_size_mb = 512
 
 [deploy]
   release_command = "migrate-db"
+  release_command_timeout = "3m"
+  [deploy.release_command_vm]
+    size = "performance-2x"
+    memory = "4G"
 
 [[restart]]
   policy = "always"
@@ -46,6 +50,3 @@ guest_path = "/guest/path"
 url_prefix = "/url/prefix"
 tigris_bucket = "example-bucket"
 index_document = "index.html"
-
-[experimental]
-machine_config = '{"dns": {"nameservers": ["1.2.3.4"]}}'
diff --git a/internal/command/deploy/machines_deploymachinesapp_test.go b/internal/command/deploy/machines_deploymachinesapp_test.go
deleted file mode 100644
index 97ea38e80..000000000
--- a/internal/command/deploy/machines_deploymachinesapp_test.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package deploy
-
-import (
-	"context"
-	"testing"
-	"time"
-
-	"github.com/stretchr/testify/assert"
-	"github.com/superfly/fly-go"
-	"github.com/superfly/flyctl/internal/appconfig"
-	"github.com/superfly/flyctl/internal/flapsutil"
-	"github.com/superfly/flyctl/internal/machine"
-	"github.com/superfly/flyctl/internal/mock"
-	"github.com/superfly/flyctl/iostreams"
-)
-
-func TestUpdateExistingMachinesWRecovery(t *testing.T) {
-	ios, _, _, _ := iostreams.Test()
-	client := &mockFlapsClient{}
-	client.machines = []*fly.Machine{{ID: "test-machine-id", LeaseNonce: "foobar"}}
-	md := &machineDeployment{
-		app:         &fly.AppCompact{},
-		io:          ios,
-		colorize:    ios.ColorScheme(),
-		flapsClient: client,
-		strategy:    "canary",
-	}
-
-	ctx := context.Background()
-	err := md.updateExistingMachinesWRecovery(ctx, nil)
-	assert.NoError(t, err)
-
-	err = md.updateExistingMachinesWRecovery(ctx, []*machineUpdateEntry{
-		{
-			leasableMachine: machine.NewLeasableMachine(client, ios, &fly.Machine{}, false),
-			launchInput:     &fly.LaunchMachineInput{},
-		},
-	})
-	assert.Error(t, err, "failed to find machine test-machine-id")
-}
-
-func TestDeployMachinesApp(t *testing.T) {
-	ios, _, _, _ := iostreams.Test()
-	client := &mockFlapsClient{}
-	webClient := &mock.Client{
-		GetAppLogsFunc: func(ctx context.Context, appName, token, region, instanceID string) (entries []fly.LogEntry, nextToken string, err error) {
-			return nil, "", nil
-		},
-	}
-	client.machines = []*fly.Machine{
-		{ID: "m1", LeaseNonce: "m1-lease", Config: &fly.MachineConfig{Metadata: map[string]string{fly.MachineConfigMetadataKeyFlyProcessGroup: "app"}}},
-		{ID: "m2", LeaseNonce: "m2-lease", Config: &fly.MachineConfig{Metadata: map[string]string{fly.MachineConfigMetadataKeyFlyProcessGroup: "app"}}},
-		{ID: "m3", LeaseNonce: "m3-lease", Config: &fly.MachineConfig{Metadata: map[string]string{fly.MachineConfigMetadataKeyFlyProcessGroup: "app"}}},
-		{ID: "m4", LeaseNonce: "m4-lease", Config: &fly.MachineConfig{Metadata: map[string]string{fly.MachineConfigMetadataKeyFlyProcessGroup: "app"}}},
-	}
-	md := &machineDeployment{
-		app:             &fly.AppCompact{},
-		io:              ios,
-		colorize:        ios.ColorScheme(),
-		flapsClient:     client,
-		apiClient:       webClient,
-		strategy:        "canary",
-		appConfig:       &appconfig.Config{},
-		machineSet:      machine.NewMachineSet(client, ios, client.machines, false),
-		skipSmokeChecks: true,
-		waitTimeout:     1 * time.Second,
-	}
-
-	ctx := context.Background()
-	ctx = iostreams.NewContext(ctx, ios)
-	ctx = flapsutil.NewContextWithClient(ctx, client)
-	err := md.deployMachinesApp(ctx)
-	assert.NoError(t, err)
-}
diff --git a/internal/command/deploy/machines_test.go b/internal/command/deploy/machines_test.go
index 6cb572154..9ef940d12 100644
--- a/internal/command/deploy/machines_test.go
+++ b/internal/command/deploy/machines_test.go
@@ -178,7 +178,6 @@ func Test_resolveUpdatedMachineConfig_ReleaseCommand(t *testing.T) {
 			},
 			Guest: fly.MachinePresets["shared-cpu-2x"],
 		},
-		SkipLaunch: true,
 	}, got)
 
 	// Update existing release command machine
@@ -228,7 +227,6 @@ func Test_resolveUpdatedMachineConfig_ReleaseCommand(t *testing.T) {
 			},
 			Guest: fly.MachinePresets["shared-cpu-2x"],
 		},
-		SkipLaunch: true,
 	}, got)
 }
 
diff --git a/internal/command/deploy/mock_client_test.go b/internal/command/deploy/mock_client_test.go
index 92430d13b..6e78fe07f 100644
--- a/internal/command/deploy/mock_client_test.go
+++ b/internal/command/deploy/mock_client_test.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"fmt"
 	"net/http"
-	"sync"
 	"time"
 
 	fly "github.com/superfly/fly-go"
@@ -22,20 +21,10 @@ type mockFlapsClient struct {
 	breakWait        bool
 	breakUncordon    bool
 	breakSetMetadata bool
-	breakList        bool
-	breakDestroy     bool
-	breakLease       bool
-
-	// mu to protect the members below.
-	mu            sync.Mutex
-	machines      []*fly.Machine
-	leases        map[string]struct{}
-	nextMachineID int
 }
 
 func (m *mockFlapsClient) AcquireLease(ctx context.Context, machineID string, ttl *int) (*fly.MachineLease, error) {
-	nonce := fmt.Sprintf("%x-lease", machineID)
-	return m.RefreshLease(ctx, machineID, ttl, nonce)
+	return nil, fmt.Errorf("failed to acquire lease for %s", machineID)
 }
 
 func (m *mockFlapsClient) Cordon(ctx context.Context, machineID string, nonce string) (err error) {
@@ -71,10 +60,7 @@ func (m *mockFlapsClient) DeleteVolume(ctx context.Context, volumeId string) (*f
 }
 
 func (m *mockFlapsClient) Destroy(ctx context.Context, input fly.RemoveMachineInput, nonce string) (err error) {
-	if m.breakDestroy {
-		return fmt.Errorf("failed to destroy %s", input.ID)
-	}
-	return nil
+	return fmt.Errorf("failed to destroy %s", input.ID)
 }
 
 func (m *mockFlapsClient) Exec(ctx context.Context, machineID string, in *fly.MachineExecRequest) (*fly.MachineExecResponse, error) {
@@ -130,24 +116,14 @@ func (m *mockFlapsClient) Kill(ctx context.Context, machineID string) (err error
 }
 
 func (m *mockFlapsClient) Launch(ctx context.Context, builder fly.LaunchMachineInput) (*fly.Machine, error) {
-	m.mu.Lock()
-	defer m.mu.Unlock()
-
 	if m.breakLaunch {
 		return nil, fmt.Errorf("failed to launch %s", builder.ID)
 	}
-	m.nextMachineID += 1
-	return &fly.Machine{
-		ID:         fmt.Sprintf("%x", m.nextMachineID),
-		LeaseNonce: fmt.Sprintf("%x-launch-lease", m.nextMachineID),
-	}, nil
+	return &fly.Machine{}, nil
 }
 
 func (m *mockFlapsClient) List(ctx context.Context, state string) ([]*fly.Machine, error) {
-	if m.breakList {
-		return nil, fmt.Errorf("failed to list machines")
-	}
-	return m.machines, nil
+	return nil, fmt.Errorf("failed to list machines")
 }
 
 func (m *mockFlapsClient) ListActive(ctx context.Context) ([]*fly.Machine, error) {
@@ -167,35 +143,11 @@ func (m *mockFlapsClient) NewRequest(ctx context.Context, method, path string, i
 }
 
 func (m *mockFlapsClient) RefreshLease(ctx context.Context, machineID string, ttl *int, nonce string) (*fly.MachineLease, error) {
-	m.mu.Lock()
-	defer m.mu.Unlock()
-
-	if m.breakLease {
-		return nil, fmt.Errorf("failed to acquire lease for %s", machineID)
-	}
-
-	if m.leases == nil {
-		m.leases = make(map[string]struct{})
-	}
-	m.leases[machineID] = struct{}{}
-
-	return &fly.MachineLease{
-		Status: "success",
-		Data:   &fly.MachineLeaseData{Nonce: nonce},
-	}, nil
+	return nil, fmt.Errorf("failed to refresh lease for %s", machineID)
 }
 
 func (m *mockFlapsClient) ReleaseLease(ctx context.Context, machineID, nonce string) error {
-	m.mu.Lock()
-	defer m.mu.Unlock()
-
-	_, exists := m.leases[machineID]
-	if !exists {
-		return fmt.Errorf("failed to release lease for %s", machineID)
-	}
-	delete(m.leases, machineID)
-
-	return nil
+	return fmt.Errorf("failed to release lease for %s", machineID)
 }
 
 func (m *mockFlapsClient) Restart(ctx context.Context, in fly.RestartMachineInput, nonce string) (err error) {