> ## Documentation Index
> Fetch the complete documentation index at: https://docs.localops.co/llms.txt
> Use this file to discover all available pages before exploring further.

# 2026

> Updates and improvements of year 2026

<Update label="October 15, 2026">
  ## Project roles, protected environments and Owner/Admin restrictions

  Until now everyone in a project could do everything in it. You can now decide exactly what each person can do, per
  project and per environment.

  ### Project roles

  Organization Members now get a role in each project they belong to:

  * **Read-only** - view the project, environments, services and deployments. No secrets or CLI.
  * **Maintainer** - everything Read-only can, plus deploy, redeploy, promote, cancel and retry, change a service's git
    branch, read logs, see secret names and use `ops logs`.
  * **Member** - full access to the project, including secret values, environment and service settings, backups, people
    and the whole CLI.

  Pick roles when you [invite someone](/team/invite-users), add people from a project's **Settings** → **Members**, and
  change roles there at any time. See [project members](/team/project-members) and the full
  [permission tables](/team/understanding-roles#permissions-in-detail).

  <Note>
    Everyone who was already in a project before this change has the **Member** role there, so nothing changes for them
    until you change it.
  </Note>

  ### Protected environments

  [Protect](/environment/protected-environments) an environment such as production to limit it to Owners and Admins with
  full access and the people you add to it. People you add get an environment role that replaces their project role there.
  Everyone else doesn't see the environment, its deployments or its operations at all.

  ### Owner and Admin restrictions

  Owners and Admins still have full access to every project, but can now be [restricted](/team/owner-admin-restrictions)
  to Member, Maintainer or Read-only in a single project or protected environment, by themselves, or by someone with the
  Member role there. They can always lift their own restriction.

  ### CLI follows your role

  `ops logs` needs the Maintainer role. `ops connect`, `ops proxy` and `ops update-kubeconfig` need Member. See [who can
  run which command](/cli/usage#who-can-run-which-command).
</Update>

<Update label="October 9, 2026">
  ## Health check timeouts, and resource tags that reach existing resources

  ### Set a timeout on health checks

  [Health checks](/environment/services/ops-json#health-checks) in `ops.json` now take a `timeout` - how long, in seconds,
  a single check can run before it counts as a failure. Leave it out and it defaults to `10` seconds.

  ```json theme={null}
  {
    "health": {
      "cmd": ["/bin/sh", "-c", "celery -A app inspect ping"],
      "delay": 180,
      "interval": 30,
      "timeout": 15,
      "failure_threshold": 10
    }
  }
  ```

  Until now every check had to answer within 1 second. A check that is slow but healthy - a `celery inspect ping`, a
  script that opens a database connection, an endpoint that does real work - could miss that window, count as a failure
  and get the container restarted for no reason. The new 10 second default covers most of these. Raise `timeout` for
  anything slower.

  It works the same for shell, HTTP, TCP and gRPC checks, and in the Helm chart you export from LocalOps.

  <Note>
    Every service with a health check picks up the new default on its next deploy, which rolls its containers once.
    Nothing restarts until you deploy.
  </Note>

  ### Fixed: account resource tags on existing environments

  [Account level resource tags](/changelogs/2026#feb-7-2026) - the key value pairs you add under **Account settings >
  Resource tags** - were only applied to environments created after you added them. Tags you added, changed or removed
  later never reached the cloud resources of environments that already existed.

  They do now. Refresh an existing environment and its resources pick up your current set of tags - added tags are
  attached, changed values are updated and removed tags are taken off. On AWS they land as tags, on GCP as labels.

  Look for the **Refresh environment** button in the top right corner of the environment dashboard.

  Cloud resources your services declare in `ops.json` - S3 buckets, RDS instances, SQS queues and the like - pick up the
  new tags on each service's next deploy.

  See [AWS resource tags](/environment/infra-resources/aws) for using tags to find your resources and analyze costs.

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="October 6, 2026">
  ## SQL Server on RDS, and Grafana share links that work

  ### SQL Server in `ops.json`

  [RDS](/environment/services/aws/rds) instances declared in `ops.json` now take SQL Server alongside Postgres and MySQL.
  Set `engine` to the edition you need - `sqlserver-ex` (Express), `sqlserver-web` (Web), `sqlserver-se` (Standard) or
  `sqlserver-ee` (Enterprise). RDS provisions it license included, so there is no license of your own to bring.

  ```json theme={null}
  {
    "dependencies": {
      "rds": {
        "instances": [
          {
            "id": "ledger",
            "prefix": "ledger",
            "engine": "sqlserver-ex",
            "storage_gb": 20,
            "exports": {
              "MSSQL_URL": "$dsn",
              "MSSQL_HOST": "$address",
              "MSSQL_USERNAME": "$username",
              "MSSQL_PASSWORD": "$password"
            }
          }
        ]
      }
    }
  }
  ```

  Deploy, and every container of the service gets those variables. `$dsn` comes out as
  `sqlserver://$username:$password@$address:1433`.

  A few things differ from Postgres and MySQL:

  * **No database is created.** RDS creates none on a SQL Server instance, so `$dbName` is not available and `$dsn` has no
    database name. Create the databases your service needs yourself, for example from an init job.
  * **At least 20 GB of storage**, which is also the default.
  * **Standard and Enterprise need an `instance_type`.** Express and Web default to `db.t3.small`.
  * **Multi-AZ** is available on Standard and Enterprise only.
  * **Encryption at rest** is on for every edition except Express, which RDS does not support it for.

  **Private by default.** Like every RDS instance LocalOps provisions, the SQL Server instance is created in the private
  subnets of your environment's VPC, with no public endpoint. Its security group accepts TCP on port `1433` only from the
  VPC's own CIDR range, so your services reach it over the private network and nothing outside the environment can. The
  credentials go to your service as environment variables. No connection string to paste into a config file, no inbound
  rule to open.

  That covers your own access too - the CLI commands below reach the database from inside the cluster, so you never have
  to make it public to query it.

  See [Private only access](/environment/services/aws/rds#private-only-access) for the networking details.

  See [SQL Server](/environment/services/aws/rds#sql-server) for the full table of editions.

  ### `ops connect` and `ops proxy` speak SQL Server

  As promised [last week](/changelogs/2026#september-29-2026), `--dep` now reaches SQL Server instances too. Open a prompt
  inside your environment:

  ```sh theme={null}
  ops connect --env us-prod --svc api --dep ledger
  ```

  The client pod runs [usql](https://github.com/xo/usql) rather than `sqlcmd`, since Microsoft publishes no `sqlcmd` image
  that runs on ARM nodes. Its prompt works like `psql` - end each statement with `;` instead of `GO`, and type `\q` to
  quit. Exit the shell and the pod is deleted.

  Or relay the instance to a port on your machine:

  ```sh theme={null}
  ops proxy --env us-prod --svc api --dep ledger
  ```

  ```
    sqlcmd -S 127.0.0.1,1433 -U appuser -C
    password: <the database password>
  ```

  While the proxy runs, point any GUI client at `127.0.0.1,1433` with those credentials - SQL Server Management Studio
  (SSMS), DataGrip, DBeaver, TablePlus or the VS Code MSSQL extension. Browse tables, run queries and inspect query plans
  on the private RDS instance as if it were running locally.

  Trust the server certificate through the tunnel - `-C` for `sqlcmd`, or **Trust server certificate** in a GUI client.
  The certificate is issued for the database's real hostname, not your machine.

  See [Connect to your databases or cache](/cli/usage#connect-to-your-databases-or-cache) for the details.

  ### Fixed: Grafana share links

  Every environment ships with its own [Grafana](/environment/monitoring/logs), wired to the environment's logs, metrics
  and cloud provider monitoring out of the box. Query logs across services, slice metrics by pod or node, build your own
  dashboards and panels, and set alerts on any of it.

  And whatever you find, you can share. **Share → Link** on a dashboard, a panel or an Explore query - down to the exact
  log lines you filtered to - gives you a unique short URL that opens the same view, with the same time range and
  filters, for whoever you send it to. Paste it into an incident channel or a ticket instead of a screenshot.

  Until now these links carried the wrong host and didn't open. They now point at your environment's Grafana domain:

  ```
  https://monitor.us-prod.example.com/goto/ef2k9xq7mzpgwd?orgId=1
  ```

  Refresh an existing environment once to pick up the fix.

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="September 30, 2026">
  ## Full size editors for ops.json and Helm values

  The `ops.json` and Helm values boxes on the service form showed a handful of lines at a time. Fine for a short config,
  cramped for anything real.

  Both now carry an **Edit** button that opens a full size editor beside the form:

  * **Line numbers** in the gutter, so a line an error message points at is easy to land on.
  * **Syntax highlighting** for JSON and YAML.
  * **Indentation markers** - a vertical guide down each nesting level, so you can tell at a glance which block a key sits
    under.
  * **Room to work** - the editor runs the full height of the window instead of the few lines the inline box showed.

  Edit freely, save, and the form picks up your changes.

  <img src="https://mintcdn.com/localops/JRMe5AvRiyD5FHvi/changelogs/images/2026/OpsFullSizeEditor.png?fit=max&auto=format&n=JRMe5AvRiyD5FHvi&q=85&s=79b3b1a37cc2ef6037b0f7069055a6b7" alt="Full size editor for ops.json" width="3146" height="2444" data-path="changelogs/images/2026/OpsFullSizeEditor.png" />

  [`ops.json`](/environment/services/ops-json) is one file, committed next to your code, that declares everything LocalOps
  sets up around a service on each deploy - init jobs, health checks, cloud resources, volumes, crons, autoscaling and
  ingress. Push a change and the environment reshapes itself to match.

  Which is why it grows, and why scrolling it a handful of lines at a time stops being workable.

  Helm values have grown for a second reason. Since
  [secrets became available for Helm chart services](/changelogs/2026#august-12-2026) in August, a `values.yaml` carries
  `existingSecret` and `secretKeys` references on top of everything the chart already ships. The full size editor gives
  you enough of the file on screen to find what you are editing.

  <img src="https://mintcdn.com/localops/JRMe5AvRiyD5FHvi/changelogs/images/2026/HelmFullSizeEditor.png?fit=max&auto=format&n=JRMe5AvRiyD5FHvi&q=85&s=23f6f9c2db4bebd104e71d8aa99c5ee8" alt="Full size editor for Helm values" width="3188" height="2456" data-path="changelogs/images/2026/HelmFullSizeEditor.png" />
</Update>

<Update label="September 29, 2026">
  ## MySQL, Redis and Valkey in the CLI, and generate FedRAMP compliant Helm charts

  ### `ops connect` and `ops proxy` now speak MySQL, Redis and Valkey

  Reaching a database inside your environment from your laptop used to work for Postgres only. A service using MySQL, or
  an [ElastiCache](/environment/services/aws/elasti-cache) Redis or Valkey cluster, needed a hand-rolled jump pod.

  Both commands now cover all of them:

  | Dependency | Engine | Shell client | Client image |
  | - | - | - | - |
  | RDS | Postgres | `psql` | `postgres:17-alpine` |
  | RDS | MySQL | `mysql` | `mysql:8.4` |
  | ElastiCache | Redis, Valkey | `redis-cli` | `redis:7-alpine` |

  `ops connect --dep` starts a short-lived client pod inside your environment, hands it the same connection details your
  service uses and opens that engine's own prompt. Exit the shell and the pod is deleted:

  ```sh theme={null}
  ops connect --env us-prod --svc api --dep app-db
  mysql>
  ```

  ```sh theme={null}
  ops connect --env us-prod --svc api --dep sessions
  redis-cli>
  ```

  `ops proxy --dep` covers the same dependencies for a local GUI client - TablePlus, DataGrip, DBeaver or RedisInsight -
  by relaying the private endpoint to a port on your machine and printing the credentials to use with it.

  `--dep` finds a database through its `$dsn` export and a cache through `$endpoint`, so your `ops.json` needs one of
  them. Memcached clusters are not supported.

  See [Connect to your databases or cache](/cli/usage#connect-to-your-databases-or-cache) for naming a specific
  dependency, overriding the client image, choosing a local port, and the SSL modes to use through a tunnel.

  **Coming next: SQL Server.** Support for SQL Server instances through the same `--dep` flag is in progress.

  ### Generate FedRAMP compliant Helm charts, with zero-CVE images

  Teams operating under FedRAMP or NIST 800-53 ask two things of every Helm chart you hand them: can it run under the
  `restricted` Pod Security Standard, and what is inside the images.

  **The Helm charts you generate support `restricted`.** Generate a chart from the LocalOps console and distribute it to
  your customers with a [license token](/api/no-access-delivery/license-tokens), which authenticates every pull of the
  chart and its private images. Pod and container security options are exposed as values, so your customer sets Pod
  Security Standards per deployment rather than patching the chart:

  ```yaml theme={null}
  podSecurityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault

  securityContext:
    allowPrivilegeEscalation: false
    readOnlyRootFilesystem: true
    capabilities:
      drop: ['ALL']
  ```

  Set only the keys you need. A workload that requires more - a host-reading agent, a process that must run as root - is
  relaxed on its own, without changing the rest.

  **Our own images carry zero CVEs.** Every image LocalOps builds and publishes is rebuilt on a minimal base and scanned
  before release, so a customer's scan during onboarding returns no findings from our side of the Helm chart.

  Services defined in `ops.json` control node level access through
  [`unsafe.privileged`](/environment/services/ops-json#privileged-containers).

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="September 28, 2026">
  ## Public API - run environments on any cloud, headless

  The LocalOps API is public. Everything you have been doing by clicking through the console - creating services, shipping
  them, rotating secrets, attaching domains - you can now do from code, with an account API token and nothing else.

  It is a small, deliberately separate surface from the console - every endpoint is listed in the
  [API reference](/api-reference). Owners and admins can read the token from the LocalOps console, and every request
  carries it:

  ```bash theme={null}
  curl -H "Authorization: Bearer $LOCALOPS_API_TOKEN" \
    https://sdk.localops.co/v1/environments/$ENV_ID
  ```

  Actions taken this way show up in your [audit log](/team/audit-logs) with the actor `API Token`.

  ### Ship from your own CI

  Create a service, then deploy a git SHA, a Docker image tag or a Helm chart version - and poll the deployment until it
  lands:

  ```bash theme={null}
  curl -X POST https://sdk.localops.co/v1/environments/$ENV_ID/services/$SERVICE_ID/deploy \
    -H "Authorization: Bearer $LOCALOPS_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"commit_id": "9f2c1ab", "note": "release 1.4.0"}'
  ```

  Send `{}` to deploy the head of the service's configured branch. The deployment id comes back immediately, and
  `GET /v1/deployments/{deploymentId}` runs to `success` or `failed`.

  ### Manage secrets and custom domains

  Read and replace secrets at the environment level or per service. Register a custom domain and the API hands back the
  exact DNS records to create, then verifies them and cuts traffic over.

  <Warning>
    Writing secrets is a full replace, not a merge. Read the whole set, change what you need and write the whole set back

    * anything you leave out is deleted.
  </Warning>

  ### Wire your own infrastructure to an environment

  `GET /v1/environments/{envId}` reads three things live from your cloud, so your DNS, firewalls and IAM policies can
  point at a LocalOps environment without anyone copying values out of a console:

  * `ingress_host` - what to point a `CNAME` (AWS) or `A` record (GCP) at
  * `public_nat_ips` - the egress addresses to allowlist in your firewalls and database security groups
  * `svc_role_arn` - the IAM role your pods assume, to name as a principal in your own bucket, queue and KMS key policies

  ### Offer bring-your-own-cloud without a console step per customer

  If you sell a [BYOC edition](/use-cases/byoc) - your product running inside each customer's own AWS, GCP or Azure
  account - onboarding has meant repeating the same console steps for every customer: provision the environment, create
  each service, wire the secrets, attach the domain, deploy. For a dev tool company signing enterprise customers, that
  loop grows linearly with the customer list, and it is the part that does not scale.

  Here is the whole thing, end to end, as API calls. Once the customer has connected their cloud account, this is a script
  you run once per customer:

  1. **Provision an environment in their cloud.** `POST /v1/environments` with the customer's cloud connection and the
     region they want - `aws-us-east-1`, `gcp-us-central1`, and so on. Poll `GET /v1/environments/{envId}` until `state`
     is `available`. *(This one endpoint is still coming soon - see below. Everything after it is live today.)*
  2. **Add your services.** `POST /v1/environments/{envId}/services` once per service - API, workers, cron jobs, frontend.
     Each one can come from a GitHub or GitLab repo, a Docker image or a Helm chart, with its own CPU, memory and replica
     counts. Adding a new service to a customer later is the same call.
  3. **Set their secrets.** `PUT /v1/environments/{envId}/secrets` for values every service shares, and
     `PUT /v1/environments/{envId}/services/{serviceId}/secrets` for the ones scoped to a single service. This is where
     per-customer configuration lives - their database URLs, their API keys, their license terms.
  4. **Deploy.** `POST /v1/environments/{envId}/services/{serviceId}/deploy` with a commit SHA, a Docker image tag or a
     Helm chart version. Poll `GET /v1/deployments/{deploymentId}` until `success` or `failed`.
  5. **Attach their domain.** `POST …/custom-domains` returns the exact DNS records for the customer to create, and
     `POST …/custom-domains/{customDomainId}/verify` checks them and cuts traffic over once they resolve.
  6. **Hand over the network details.** `GET /v1/environments/{envId}` gives you the `ingress_host` to point DNS at, the
     egress IP addresses to allowlist and the IAM role to name in their resource policies - the three things their network
     and security teams ask for during review.

  From then on, shipping a release to every customer you have is the same program with a different environment id:

  ```bash theme={null}
  while read -r ENV_ID SERVICE_ID; do
    curl -X POST https://sdk.localops.co/v1/environments/$ENV_ID/services/$SERVICE_ID/deploy \
      -H "Authorization: Bearer $LOCALOPS_API_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"docker_image_tag": "1.4.0", "note": "rollout 1.4.0"}'
  done < customer-envs.txt
  ```

  Each call hands back a deployment id, so you can gate the next customer on the previous one reaching `success` - a
  staged rollout across your install base, written in whatever your team already uses.

  ### What is next

  Per-operation status and logs, so a failure tells you why. Creating and deleting environments. Deleting a custom domain.
  Cancelling an in-flight deployment. All of them are already published in the [API reference](/api-reference) with a
  **`COMING SOON`** badge, so you can design against them today - they return `404` until they ship.

  ### Next up: MCPs

  This API is the foundation for what we are building next - a LocalOps MCP server, so your agents can deploy on your
  behalf.

  The same operations you have just read about, exposed as tools an agent can call: create a service, set its secrets,
  ship a release, check what state an environment is in. Ask your agent to stand up a service for a branch, roll a new
  image tag out to a customer, or tell you why last night's deployment failed, and it talks to LocalOps directly instead
  of you translating the request into `curl`.

  This and everything else we are working on is tracked on our
  [public roadmap](https://github.com/orgs/localopsco/projects/9).

  [Get started](/api/getting-started) · [Common workflows](/api/workflows) · [API reference](/api-reference)

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="September 11, 2026">
  ## New options in ops.json: bigger uploads, fewer surprise errors

  We have raised the platform limits that teams most often hit when they onboard, so a normal app should now run without
  touching any of this. Four new `ops.json` options let you set your own limits when your service needs more.

  ### `request_body_size`: bigger uploads work out of the box

  Every environment's ingress used to reject any request body over 1 MB, so an ordinary file upload came back as a
  `413 Request Entity Too Large` before it ever reached your container. The platform now accepts **30 MB** by default, in
  line with what other hosting platforms allow.

  If your service takes bigger uploads - attachments, media, bulk imports - raise the limit yourself with
  `request_body_size`, in whole MB:

  ```json ops.json theme={null}
  {
    "request_body_size": 100
  }
  ```

  Anything up to 1 GB is allowed. [Read the docs](/environment/services/ops-json#request-body-size).

  ### `proxy_buffer_size` and `proxy_buffers`: fix `502`s caused by large response headers

  Apps that hand back long headers - SSO redirects, big session cookies, fat JWTs - could fail with a `502 Bad Gateway`,
  because the ingress reserved only a small buffer for a response's headers. That buffer is now bigger by default, and you
  can size it yourself when your app needs more:

  ```json ops.json theme={null}
  {
    "proxy_buffer_size": 32,
    "proxy_buffers": 16
  }
  ```

  `proxy_buffer_size` is the size of each buffer in KB and is the one that fixes the `502`. `proxy_buffers` is how many of
  them the ingress uses while streaming a response, and is usually fine left alone.
  [Read the docs](/environment/services/ops-json#response-buffers).

  ### `send_timeout`, `read_timeout` and `connection_timeout`: slow requests get more room

  The ingress waits longer before giving up on your service. Sending and reading now allow **120 seconds** each, up from
  60, and connecting to your container is capped at 10 seconds so a genuinely dead container fails fast instead of
  hanging. Slow reports, large downloads and chatty websocket handshakes stop getting cut off for no obvious reason.

  You can still set `send_timeout`, `read_timeout` and `connection_timeout` per service, up to an hour.
  [Read the docs](/environment/services/ops-json#request-timeouts).

  ### `deploy_timeout`: give slow-starting services longer to deploy

  A deployment waits 5 minutes for your new version to become ready, then fails. That is too short for services with large
  images or heavy startup work, so you can now extend it:

  ```json ops.json theme={null}
  {
    "deploy_timeout": 900
  }
  ```

  Anything from 1 to 20 minutes is allowed. Every deployment logs the wait it is using, so you can confirm your value was
  picked up. Services on Windows node groups already get 15 minutes by default.
  [Read the docs](/environment/services/ops-json#deploy-timeout).

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="September 9, 2026">
  ## CLI v3.1.0 - Connect with pg shell or set up port forwarding

  We added two new commands that let you connect to the RDS Postgres shell directly from your dev laptop. All without
  signing into bastion servers or any other step.

  ```sh theme={null}
  ops connect --env prod --svc web --dep appdb
  ```

  Above command opens a postgres shell so you can start querying the database.

  ```sh theme={null}
  ops proxy --env prod --svc web --dep appdb
  ```

  This command sets up port-forwarding so you can use GUI tools to connect to the same postgres database with host set to
  localhost and port set to 5432.

  All of this will soon work for redis, mysql and sql server too! Stay tuned!

  #### New CLI menu

  <img src="https://mintcdn.com/localops/rm3UGDPxIJ_T-pDO/changelogs/images/2026/pgshell.png?fit=max&auto=format&n=rm3UGDPxIJ_T-pDO&q=85&s=203e1913bc72009bb1c32bbd2390ca22" alt="Build context" width="1612" height="1200" data-path="changelogs/images/2026/pgshell.png" />

  You can find these commands to copy-paste, from the new "CLI" tab in Environment/Service dashboard.

  #### Install/upgrade to v3.1.0

  Both commands ship in CLI **v3.1.0**. If you're new to the CLI, install it with Homebrew and sign in:

  ```sh theme={null}
  brew install localopsco/tap/ops
  ops login
  ```

  Already have `ops`? Upgrade in place:

  ```sh theme={null}
  brew update && brew upgrade ops
  ```

  Both commands take more options than shown here, like `-i`. See the [CLI docs](/cli/usage) for the full list, and for
  the rest of the commands.

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="September 7, 2026">
  ## CLI v3.0.4 — Shell into a service and tail its logs with one command

  Getting a shell inside a running service used to mean pointing your kubeconfig at the environment, listing pods in
  `app-services` and picking the right one before `kubectl exec`. Two new CLI commands cut that down to a single line
  each.

  ```sh theme={null}
  ops connect --env "ProdUS" --svc web
  ```

  opens a shell in your service, so you can do ad-hoc script runs or drop into your Django / Rails / other application
  shell.

  ```sh theme={null}
  ops logs --env "ProdUS" --svc web
  ```

  tails logs in real time. You don't have to open Grafana just to watch what a service is printing.

  #### New shell tab

  <img src="https://mintcdn.com/localops/p2t_xmMVVULf-mnS/changelogs/images/2026/CLI-release-screenshot.png?fit=max&auto=format&n=p2t_xmMVVULf-mnS&q=85&s=6c91e19eefdcdf32f10baeec33dacfd5" alt="Build context" width="2408" height="1652" data-path="changelogs/images/2026/CLI-release-screenshot.png" />

  You can find these commands to copy-paste, from the new "Shell" tab in Service dashboard.

  #### Install/upgrade to v3.0.4

  Both commands ship in CLI **v3.0.4**. If you're new to the CLI, install it with Homebrew and sign in:

  ```sh theme={null}
  brew install localopsco/tap/ops
  ops login
  ```

  Already have `ops`? Upgrade in place:

  ```sh theme={null}
  brew update && brew upgrade ops
  ```

  Both commands take more options than shown here, like `-i`. See the [CLI docs](/cli/usage) for the full list, and for
  the rest of the commands.

  ## Cluster your BEAM nodes across regions

  Elixir services running in separate regional environments can now form a single cluster. Keep `beam_cluster_id`
  identical everywhere, and give each environment its own record in a private DNS zone that every regional VPC can
  resolve:

  ```json ops.json theme={null}
  {
    "beam_cluster_id": "b2bapp",
    "beam_dns_name": "mumbai.mesh.acmecorp.internal"
  }
  ```

  LocalOps creates that record and keeps it pointed at the environment's pods as they scale, restart and get rescheduled.
  Add one libcluster topology per region — the local nodes via `LOPS_BEAM_CLUSTER_DNS`, each peer region by its
  `beam_dns_name` — and the mesh forms. The regional VPCs still need routable connectivity between them.
  [Read the docs](/environment/services/beam-clustering#cluster-across-regions).

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="September 2, 2026">
  ## Monorepos fully supported with custom Build Context path

  <img src="https://mintcdn.com/localops/wUn86UMkTomMoH9d/changelogs/images/2026/buildcontext.png?fit=max&auto=format&n=wUn86UMkTomMoH9d&q=85&s=897af41a8222c418baa60b7e8da7f9c5" alt="Build context" width="2664" height="2257" data-path="changelogs/images/2026/buildcontext.png" />

  In favor of simplicity, teams host all their code within a single git repository. And organize subsystems within
  multiple sub-directories like client/, server/, ui/ etc., each having their own Dockerifle.

  So far, you can give a custom Dockerfile path while creating services - like client/Dockerfile, server/Dockerfile and so
  on.

  From today, you can also set a custom build context path to inform our plaform where to trigger `docker build` from.
  Either from the root of the git repo (default option), or from any other path.

  ```
  docker build -f <DockerfilePath> -t ImageName:ImageTag <BuildContextPath>
  ```

  ## EKS control plane logs visible within Grafana

  EKS clusters managed via LocalOps platform will now start to log all its control plane logs to CloudWatch which are in
  turn visible in the in-built grafana dashboard.

  So your advanced users get to understand how the underlying cluster works, whenever needed.

  ### Other enhancements:

  1. Increased project description length to let teams describe their projects in full
  2. Several security enhancements to zero out CVEs the 3rd party docker images

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="August 28, 2026">
  ## Persist data, mount certs, and grab scratch space

  Your service may need scratch space for temporary files, a durable disk that survives restarts, or certificate files
  mounted at a well known path. Declare all three as volumes in `ops.json` and LocalOps mounts them for you:

  ```json theme={null}
  {
    "volumes": [
      { "name": "scratch", "mount_path": "/tmp/scratch", "kind": "mem", "size": "512Mi" },
      { "name": "data", "mount_path": "/var/lib/data", "kind": "disk", "size": "10Gi", "expose": true },
      { "name": "certs", "mount_path": "/etc/certs", "kind": "secret", "secret_name": "my-tls-certs" }
    ]
  }
  ```

  `mem` is scratch discarded at shutdown — add a `size` to make it a RAM disk. `disk` is encrypted, persists across
  restarts and gets one disk per replica. `secret` mounts a Kubernetes secret read-only, each key becoming a file. Disk
  volumes aren't available on cron and job services, and support covers AWS and GCP.

  The same declaration drives the Helm chart you generate for customers. Volumes marked `expose: true` land in the chart's
  `values.yaml` under `persistence`, so whoever installs it can size the disk or point at their own secret:

  ```yaml theme={null}
  api:
    persistence:
      # /var/lib/data
      'data':
        size: 10Gi
  ```

  Everything else stays fixed — mount paths and kinds are part of how your app works, not an install-time choice.
  [Read the docs](/environment/services/ops-json#volumes).

  ## Let your Elixir nodes find each other

  Phoenix PubSub, a distributed registry, or any coordinated work across replicas needs your BEAM nodes to discover each
  other first. Add one field to `ops.json`, using the same value in every service that belongs together:

  ```json theme={null}
  {
    "beam_cluster_id": "myapp"
  }
  ```

  Each service gets `LOPS_BEAM_CLUSTER_NAME` and `LOPS_BEAM_CLUSTER_DNS` injected at runtime — point libcluster's
  Kubernetes DNS strategy at the latter, and set `RELEASE_DISTRIBUTION`, `RELEASE_NODE` and `RELEASE_COOKIE` yourself.
  Rebuild to apply, and note that previews cluster separately from production.
  [Read the docs](/environment/services/beam-clustering).

  ## Run VMs inside your services on GCP

  Firecracker microVMs, Android emulators, CI runners that build and boot disk images — workloads that need a real VM
  couldn't run on LocalOps before, because the underlying nodes didn't permit a guest hypervisor.

  Now you can enable nested virtualization on a GCP node group from the Node groups section, picking an Intel-based
  machine type, then assign the services that need VMs to it. KVM is available inside the container, so anything shelling
  out to QEMU/KVM behaves as it would on a bare VM.

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="August 20, 2026">
  ## Restrict who can reach your services with IP allowlists

  <img src="https://mintcdn.com/localops/AWhHRHiwaFo2wKAN/changelogs/images/2026/service-ip-whitelist.png?fit=max&auto=format&n=AWhHRHiwaFo2wKAN&q=85&s=ec73ed11dcd04bb27841b8ce52da043e" alt="Service IP whitelist" width="2386" height="2448" data-path="changelogs/images/2026/service-ip-whitelist.png" />

  Some of your services shouldn't be open to the whole internet. An internal admin panel, a partner-facing API, a staging
  web app — you want these reachable only from your office network, your VPN, or a specific customer's egress IPs.

  There's now a **Security** tab in the service dashboard where you can do exactly that.

  Add the IP addresses and CIDR ranges that should be allowed to reach the service, and everything else gets turned away
  at the ingress:

  ```
  203.0.113.0/24
  198.51.100.7
  10.20.0.0/16
  ```

  Leave the list empty and the service stays open to all IPs — which is how every service behaves today, so nothing
  changes until you explicitly add entries.

  ### Preview services and cloned environments

  Preview services inherit the allowlist from their parent service automatically, so a locked-down service stays locked
  down across its previews. Cloning an environment also copies each service's allowlist to the clone. Neither needs
  anything from you.

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="August 12, 2026">
  ## Passing secrets securely for Helm Charts

  Secrets section is now available for Helm Chart services too!

  <img src="https://mintcdn.com/localops/3DzdPM7SwBTHxvw_/changelogs/images/2026/helm-secrets.png?fit=max&auto=format&n=3DzdPM7SwBTHxvw_&q=85&s=4d17f34edefc0146c6a55116af775a06" alt="Chart secrets" width="2886" height="1264" data-path="changelogs/images/2026/helm-secrets.png" />

  So teams can now create secrets for Helm charts in the secrets editor. This will encrypt and save secrets in AWS
  parameter store (in the case of AWS) and save them as secret in Kubernetes cluster.

  And in values.yaml passed as setting in Helm chart service, the k8s secret can be referenced. Like this, for example
  with postgers chart:

  ```yaml theme={null}
  auth:
    existingSecret: 'db-adm69f6-secrets'
    secretKeys:
      adminPasswordKey: 'postgrespassword'
      userPasswordKey: 'password'
  ```

  You can find the `existingSecret` name in secrets section.

  ...instead of passing them in plaintext like this:

  ```yaml theme={null}
  auth:
    postgresPassword: 'mysecretpass'
    username: 'myuser'
    password: 'myuserpass'
    database: 'mydb'
  ```

  ### Custom domain for chart services

  Say your helm chart exposes services to the public world. For eg - you're self-hosting Supabase which has a dashboard
  that must be exposed to the world.

  New "Custom domain" section in the service dashboad will now provide instructions on how to expose this service.

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="August 7, 2026">
  ## New search bar

  Managing more than a handful of services in your environment?

  Checkout the new search bar in services section that lets you search for services by their name or branch name or
  anything you remember about them.

  <img src="https://mintcdn.com/localops/oVO-jAPycqTm3LNt/changelogs/images/2026/services-search.png?fit=max&auto=format&n=oVO-jAPycqTm3LNt&q=85&s=2251bbd291477f91a5cfd9d3d9a2e5f1" alt="Search services" width="1872" height="774" data-path="changelogs/images/2026/services-search.png" />

  ### New demo section

  When users sign up for first time, they are greeted a new brand new demo section that takes them live through the core
  product UX in just 2 mins.

  * create environment
  * deploy services
  * repeat!

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="July 29, 2026">
  ## Bug fixes

  We sent a bunch of fixes this time, as reported by our users:

  * Fixed an issue where console refreshes adhoc when picking a repo in service form
  * Hide the replica count when service is stopped and avoid showing "0/0" as count
  * Post service creation, showed an appropriate message to ask user to deploy with correct branch name
  * Allowed user to picker large cpu and memory rquirements for their service(s) under Scaling tab
  * Several other minor bug fixes and UI refinements throughout the console

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="July 28, 2026">
  ## Environment cloning fixes

  A few fixes to make cloning an environment more reliable:

  * **Clone environments across projects.** You can now clone an environment into a different project, not just within the
    same project — handy for promoting a setup from one project to another.
  * **More reliable global secrets page.** The global secrets page in a cloned environment now loads consistently on the
    first try instead of occasionally appearing empty, and the page stays safely locked until your secrets have fully
    loaded.
  * **Service references in `ops.json` are now rewritten correctly.** When you clone an environment, service alias
    references (e.g. `$svc-123`) in `ops.json` are updated to point at the corresponding services in the new environment —
    matching how secrets are already remapped.
  * **Helm charts are no longer skipped.** Helm-based services are now reliably included when cloning an environment,
    instead of occasionally being left out.

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="July 17, 2026">
  ## Lots of little fixes (and some tighter access control)

  Team and account management endpoints now enforce **owner/admin roles**. Inviting or removing members, cancelling
  invitations, changing member roles, listing members and invitations, managing API tokens, registry and license-token
  access, and account domain changes are all now restricted to owners and admins. Members without the right role receive a
  clear permission error instead of the action going through.

  ## Clearer error messages

  * Domain verification failures now return a specific `DOMAIN_VERIFICATION_FAILED` error instead of a generic 4xx, so you
    can tell exactly what went wrong.
  * Verbose internal authentication validation errors are no longer surfaced in API responses, keeping error output clean
    and actionable.

  ## Fixes

  * Environment creation could sometimes fail in fresh Google Cloud projects — this has been fixed.
  * Private instance lookups and updates are more robust — consistent handling of not-found instances and a fix for an
    edge case when updating an instance with no changed fields.
  * Fixed the LocalOps CLI rendering CLI flags: the Geist Mono ligature no longer swallows the space before double-dash
    (`--`) flags.

  No new buttons to click this week — just a bunch of things that quietly stopped misbehaving. Your future self, running
  `deploy` at 2am, thanks us.

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="July 3, 2026">
  ## Google Cloud support

  <img src="https://mintcdn.com/localops/cKuh7SSJ4WHCOy0q/changelogs/images/2026/gcp-support-july-3.png?fit=max&auto=format&n=cKuh7SSJ4WHCOy0q&q=85&s=3230cc40849a78d41a08d1404ba9a9ac" alt="Connect a Google Cloud account in LocalOps" width="1010" height="1198" data-path="changelogs/images/2026/gcp-support-july-3.png" />

  You can now spin up environments and deploy services on **Google Cloud** — just the way it works on AWS. The underlying
  cloud specifics are abstracted out, so your teams get the same developer experience regardless of whether they run on
  Google Cloud or AWS.

  To get started, add a new connection, pick **Google Cloud**, and follow the setup flow. See
  [Connect a Google Cloud account](https://docs.localops.co/accounts/gcp) for the full walkthrough.

  If you don't see Google Cloud as an option, write to us at [support@localops.co](mailto:support@localops.co) to get access.

  ## Update the CLI to v3.0.3

  To use Google Cloud support, update the LocalOps CLI to **v3.0.3**. See the
  [CLI install guide](https://docs.localops.co/cli/usage) for how to update.

  Questions? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="June 30, 2026">
  ## GitLab support

  You can now connect your GitLab repositories to LocalOps and enable continuous deployments on any cloud — your own cloud
  or your customer's cloud. Once connected, every push triggers a deployment through the same pipeline you already use, so
  GitLab-hosted teams get the full BYOC, single-tenant and self-hosted experience without changing where their code lives.

  ## Changelog for each Helm chart version

  When generating a Helm chart, you can now add a changelog describing what changed in that version. The changelog is
  optional, supports Markdown, and appears in the chart's release history — so the teams installing your chart can see
  exactly what each version brings.

  <img src="https://mintcdn.com/localops/vgLDSQJGkcHh6kD2/changelogs/images/2026/helm-chart-changelog-publish-june-30.png?fit=max&auto=format&n=vgLDSQJGkcHh6kD2&q=85&s=079236d2aebfdd35374de4c57972627d" alt="Add a changelog when publishing a Helm chart version" width="1190" height="1344" data-path="changelogs/images/2026/helm-chart-changelog-publish-june-30.png" />

  The changelog shows up against every version under **Available Versions**, with a short summary and the full text
  expandable inline.

  <img src="https://mintcdn.com/localops/vgLDSQJGkcHh6kD2/changelogs/images/2026/helm-chart-changelog-versions-june-30.png?fit=max&auto=format&n=vgLDSQJGkcHh6kD2&q=85&s=377a1ab39ebaece903b6a9792de6946a" alt="Helm chart versions with their changelogs" width="1514" height="1196" data-path="changelogs/images/2026/helm-chart-changelog-versions-june-30.png" />

  Questions about either feature? Email us at [support@localops.co](mailto:support@localops.co) or ping us in your Slack Connect support channel.
</Update>

<Update label="June 16, 2026">
  ## Reading the real client IP

  New doc explaining which header to trust for the real client IP based on how your service is fronted. The short version:

  | Setup | Read this header |
  | - | - |
  | No CDN, HTTP | `X-Real-IP` (or `X-Forwarded-For`) |
  | No CDN, HTTPS (via SSL-passthrough multiplexer) | `X-Real-IP` (or `X-Forwarded-For`) |
  | Behind CDN | `X-Forwarded-For[0]` — or the CDN's own header like `CF-Connecting-IP` / `True-Client-IP` |

  See [Client IP](https://docs.localops.co/environment/services/client-ip) for the full guidance, spoofing notes, and Node
  / Python / Go / Elixir examples.
</Update>

<Update label="June 12, 2026">
  ## New warning when marking a Helm chart field as non-sensitive

  When editing a Helm chart's values in the console, switching a previously sensitive field to non-sensitive now prompts a
  confirmation dialog listing every affected key. This guards against accidentally exposing credentials or other
  confidential data — once a value is marked non-sensitive, it is stored and rendered in plain text.

  <img src="https://mintcdn.com/localops/uD6K0jy5tvbJZl1-/changelogs/images/2026/non-sensitive-secret-warning-june-12.png?fit=max&auto=format&n=uD6K0jy5tvbJZl1-&q=85&s=a52e7df69b3d2467de09e97ee8cf8f6e" alt="Mark a secret as non-sensitive confirmation dialog" width="1256" height="680" data-path="changelogs/images/2026/non-sensitive-secret-warning-june-12.png" />
</Update>

<Update label="June 9, 2026">
  ## `DEPLOYMENT_ID` and `LOPS_INSTANCE_ID` built-in secrets

  Every service now receives `DEPLOYMENT_ID` and `LOPS_INSTANCE_ID` as built-in environment variables. Both carry the same
  24-character alphanumeric value (as unique as a UUIDv4) — unique per LocalOps-managed environment, and unique per Helm
  install of charts generated by LocalOps. Use either to tag logs, metrics or external resources with the originating
  environment.

  See [Built-in secrets](https://docs.localops.co/environment/services/secrets#built-in-secrets) for the full list.
</Update>

<Update label="June 5, 2026">
  ## Import environment

  You can now create a new environment from one or more existing environments in just a few clicks via **Import
  environment**. In a brand new environment, click the 3-dots on the top right and choose "Import from environment". Pick
  a source environment and import — all services, secrets and cloud resources (RDS, ElastiCache, S3, etc.) are copied
  over, with inter-dependencies preserved, so you can start deploying as soon as the import finishes.

  <img src="https://mintcdn.com/localops/IwYzURPSFAXMaUrE/changelogs/images/2026/import-env-dropdown-june-5.png?fit=max&auto=format&n=IwYzURPSFAXMaUrE&q=85&s=510b54c4fa98e9ffc77d7eda01349163" alt="Import from environment menu" width="1022" height="610" data-path="changelogs/images/2026/import-env-dropdown-june-5.png" />
</Update>

<Update label="June 2, 2026">
  ## `managed_password` toggle for RDS

  RDS instances in `ops.json` now accept a `managed_password` boolean. The default is `true`, where AWS RDS generates the
  master password, stores it in its own AWS Secrets Manager entry, and rotates it automatically. Setting it to `false`
  makes LocalOps generate a cryptographically unique string and use it as the RDS master password instead — the password
  is then not rotated, so this mode is not recommended. Regardless of which mode you pick, use `$password` and `$dsn` in
  `exports` to fetch the current password and connection string; LocalOps resolves them transparently in both cases.

  ```json theme={null}
  {
    "dependencies": {
      "rds": {
        "instances": [
          {
            "id": "test123",
            "prefix": "testdep123",
            "engine": "postgres",
            "managed_password": true,
            "exports": {
              "MY_RDS_INSTANCE_PASSWORD": "$password",
              "MY_RDS_INSTANCE_DSN": "$dsn"
            }
          }
        ]
      }
    }
  }
  ```

  See [RDS](https://docs.localops.co/environment/services/aws/rds) for details.
</Update>

<Update label="May 28, 2026">
  ## OpenSearch as a dependency

  You can now declare Amazon OpenSearch domains under `dependencies.opensearch.domains` in `ops.json`. LocalOps provisions
  production-tuned domains by default — 3-node multi-AZ quorum, encryption at rest, node-to-node encryption, HTTPS-only
  with TLS 1.2, fine-grained access control with an auto-generated master user in AWS Secrets Manager, and audit logging
  to CloudWatch. Preview environments are automatically scaled down to a single node. See
  [OpenSearch](https://docs.localops.co/environment/services/aws/opensearch) for the full reference.

  ```json theme={null}
  {
    "dependencies": {
      "opensearch": {
        "domains": [
          {
            "id": "productsearch",
            "prefix": "product-search",
            "engine_version": "OpenSearch_2.11",
            "instance_type": "t3.small.search",
            "node_count": 3,
            "exports": {
              "PRODUCT_SEARCH_DSN": "$dsn"
            }
          }
        ]
      }
    }
  }
  ```

  ## `parameters` for RDS and ElastiCache

  Both RDS instances and ElastiCache clusters now accept a `parameters` list in `ops.json` to configure DB / cache
  parameter group settings inline. LocalOps creates a dedicated parameter group for the resource and keeps it in sync as
  you add, update or remove entries on subsequent deployments.

  For RDS, each entry takes a `name`, `value` and an `apply_method` of `immediate` or `pending-reboot` (use the latter for
  parameters like `shared_preload_libraries` that need a restart):

  ```json theme={null}
  {
    "parameters": [
      {
        "name": "shared_preload_libraries",
        "value": "pg_stat_statements,pg_cron",
        "apply_method": "pending-reboot"
      }
    ]
  }
  ```

  For ElastiCache, each entry is just a `name` and `value`:

  ```json theme={null}
  {
    "parameters": [
      {
        "name": "maxmemory-policy",
        "value": "volatile-lru"
      }
    ]
  }
  ```

  See [RDS](https://docs.localops.co/environment/services/aws/rds) and
  [ElastiCache](https://docs.localops.co/environment/services/aws/elasti-cache) for details.

  ## Plaintext `$password` and `$dsn` exports for RDS

  RDS exports now include `$password` and `$dsn` alongside the existing `$passwordArn`. `$password` resolves the master
  user password from AWS Secrets Manager and injects it directly as an environment variable, so your code does not need to
  call the Secrets Manager API. `$dsn` is a ready-to-use connection string in the form
  `postgres://$username:$password@$address:5432/$dbName` (or the MySQL equivalent) for libraries that accept a single URL.
  Treat both values as secrets.

  ```json theme={null}
  {
    "exports": {
      "MY_RDS_INSTANCE_PASSWORD": "$password",
      "MY_RDS_INSTANCE_DSN": "$dsn"
    }
  }
  ```

  ## Import managed secrets from other services

  Secrets exported via `ops.json` now appear in each service's console secrets section as **managed secrets**. You can
  pull those exports into any other service in the same environment as a single secret — every key the source service
  exported is injected as environment variables in the importing service.

  This is useful for wiring databases, caches and other declared cloud resources into the services that consume them. For
  example, a boot script job can declare an RDS instance in `ops.json` and export its DSN, username and password; any
  other service in the same environment can then import them all in one line. See
  [Importing other service secrets](https://docs.localops.co/environment/services/secrets#importing-other-service-secrets)
  for the syntax.

  ## Search in the service switcher

  The service switcher now has a search bar at the top, so you can jump to a service by typing instead of scrolling. Handy
  in environments with a long list of services.
</Update>

<Update label="May 22, 2026">
  ## Deletion protection for critical services

  You can now turn on **deletion protection** for a service from its settings. Once enabled, the service cannot be deleted
  until protection is explicitly turned off — a safety net for production and other critical services where an accidental
  delete could cause downtime.
</Update>

<Update label="May 21, 2026">
  ## ✋ Improvements and bug fixes

  We have shipped a handful of bug fixes and small enhancements today to make day-to-day operations smoother. These are
  the kind of polish that adds up over time. Read on for the highlights.

  #### <Icon icon="square" iconType="solid" color="#dc2626" /> Stop and start services on demand

  Each service dashboard now has a **Stop** button in the top right corner. Use it to scale down a service's
  replica/container count to `0` on demand, without deleting the service or its configuration. Click start button to bring
  it back up.

  #### Consistent image tag on redeploys

  After updating secrets or scaling settings, users can trigger a new deployment. For docker image based services, this
  deployment was previously pulling the image with the `latest` tag. It now uses the exact image tag from the previous
  deployment, so redeploys stay consistent with what was last running.
</Update>

<Update label="May 19, 2026">
  ## Valkey support for ElastiCache

  ElastiCache clusters declared in `ops.json` can now use `valkey` as the engine, alongside `redis` and `memcache`. Pick a
  supported version from the
  [AWS Valkey versions list](https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/supported-engine-versions-valkey.html).
  See [ElastiCache](https://docs.localops.co/environment/services/aws/elasti-cache) for details.

  ```json theme={null}
  {
    "dependencies": {
      "elasticache": {
        "clusters": [
          {
            "id": "test123",
            "prefix": "testdep123",
            "engine": "valkey",
            "version": "8.0",
            "instance_type": "cache.t4g.small",
            "num_nodes": 1
          }
        ]
      }
    }
  }
  ```
</Update>

<Update label="May 14, 2026">
  ## SSL passthrough

  Services can now handle TLS termination themselves instead of letting the environment's nginx ingress decrypt traffic.
  Set `ssl_passthrough` to `true` in `ops.json` and configure your service's port to `443` so the ingress forwards raw TLS
  connections directly to your container. Useful for mTLS, custom certificate handling, or any protocol requiring
  end-to-end TLS. See [SSL passthrough](https://docs.localops.co/environment/services/ops-json#ssl-passthrough) for
  details.

  ```json theme={null}
  {
    "ssl_passthrough": true
  }
  ```
</Update>

<Update label="May 13, 2026">
  ## Instrument services with internal /metrics endpoint

  You can record custom metrics and expose them to in-built prometheus monitoring stack via `/metrics` endpoint. Just add
  an ops.json file in your repository and declare "metrics" configuration as documented here -
  [Instrument services](https://docs.localops.co/environment/services/instrument-service) to get started.

  ```
  {
      "metrics": {
          "endpoint": "/metrics",
          "interval": 15,
          "port": 9090
      }
  }
  ```

  This can include:

  * language run time metrics - NodeJS event loop metrics, JVM metrics, etc.,
  * connection and request metrics like "tcp\_connections\_open"
  * business metrics like "number\_of\_payments"
  * transactional metrics like "number\_of\_emails\_sent"

  <Note>
    Pro tip - Checkout the language specific guides to learn how you can unveil advanced metrics & dashaboards in just few
    mins of setup (eg: [See this Java guide](https://docs.localops.co/environment/services/instrument/java))
  </Note>

  ## Request timeouts

  Set timeouts for TCP connections or requests handled by your services. Default timeout 60s may not be suitable for
  realtime applications with long running connections. Start declaring custom timeout values in ops.json as specificed in
  developer docs - [Request timeouts](https://docs.localops.co/environment/services/ops-json#request-timeouts).

  ### Private IPs passed as environment vars

  `POD_IP` and `HOST_IP` are now injected in each service as environment variables. So if your application requires its
  own IP address, you can fetch and utilize in your business logic.

  Learn more about [built-in environment vars](https://docs.localops.co/environment/services/secrets#built-in-secrets)
  like these.

  ### Other UI enhancements and fixes

  1. Saving secrets triggers a "Write secrets" op to keep track of secret updates
  2. While creating new service, search for repositories using the new search bar in the repository picker
  3. Bug fixes in Member invite workflow
  4. Bug fixes in preview environments
  5. Other fixes in slack and ms teams notificaitons
</Update>

<Update label="April 18, 2026">
  ## Projects revampled

  We have revamped Projects. You can now create projects and organize environments in them.

  ### Members

  Projects can have members. Unless an org member is also added as a project member, they can't see the project's
  environments or deploy to them.

  ### Revampled navigation bar

  Sidebar navigation menu is revamped to make it easy to switcher between organization and projects.

  Other bug fixes and UI enhancements were made too.
</Update>

<Update label="April 9, 2026">
  ## Organization SSO (SAML 2.0)

  We’ve introduced Single Sign-On (SSO) via SAML 2.0, allowing teams to manage access through their preferred identity
  providers. This feature simplifies user onboarding and enhances security by centralizing authentication.

  <img src="https://mintcdn.com/localops/CADRqCygyEYxhxg4/changelogs/images/2026/sso-saml-2.0.png?fit=max&auto=format&n=CADRqCygyEYxhxg4&q=85&s=d7f4ff7d3e1d9c9599efdd3d40231a73" alt="saml 2.0" style={{ height: '400px', width: '100%' }} width="1456" height="1137" data-path="changelogs/images/2026/sso-saml-2.0.png" />

  Any SAML2.0 compliant identity provider is supported through this SAML2.0.

  * Okta
  * Microsoft Entra ID
  * Google Workspace SSO
  * OneLogin
  * Auth0

  **Availability: Now available for all Business Plan customers**

  > What’s Next: OIDC (OpenID Connect) support is currently in development and will be released soon.

  #### Improved CLI Organization Management

  Users who belong to multiple organizations can now explicitly select their active organization directly within the
  Command Line Interface (CLI). This ensures that commands and deployments are always targeted at the correct environment
  without manual configuration overrides.

  #### Simplified Manual Deployments

  To streamline the redeployment process, we’ve added a new option in the manual deployment section. You can now quickly
  select and use the last successfully deployed Docker or Helm images, reducing the risk of version mismatch during quick
  fixes or rollbacks.

  #### Security Improvements

  We’ve strengthened the security of our OAuth device authorization flow to further using stricter state validation and
  session integrity.
</Update>

<Update label="March 31, 2026">
  ## Switch organizations/teams & New CLI update

  If you run multiple products and multiple engineering teams handling their own qa, uat and production environments, you
  will love this update.

  **Switch organizations:**

  Users can now belong to multiple organizations using their same login / email address. And they can easily switch
  between the organizations from the top left menu like this:

  <img src="https://mintcdn.com/localops/eqO0sOcHPRQksUxy/changelogs/images/2026/switch-org.png?fit=max&auto=format&n=eqO0sOcHPRQksUxy&q=85&s=238a80289b5e01637a520a4b105af94b" alt="switch organization" style={{ height: '400px', width: '100%' }} width="1412" height="985" data-path="changelogs/images/2026/switch-org.png" />

  Each organization can have unique environments like this:

  * Github org
  * ECR Registries
  * Environments
    * qa
    * uat
    * production
  * Deployments

  **Enhanced CLI Login:**

  LocalOps CLI now use [device auth](https://datatracker.ietf.org/doc/html/rfc8628) for more seamless and secure login. To
  login, just type this in your terminal:

  ```bash theme={null}
  ops login
  ```

  <img src="https://mintcdn.com/localops/eqO0sOcHPRQksUxy/changelogs/images/2026/cli-device-auth.png?fit=max&auto=format&n=eqO0sOcHPRQksUxy&q=85&s=2d7e2bd56781949085ef66d960790ac3" alt="cli device auth" style={{ height: '450px', width: '100%' }} width="1456" height="1322" data-path="changelogs/images/2026/cli-device-auth.png" />

  And you will get a link to click and open the browser. If you’re already logged in to LocalOps console
  (console.localops.co), we will show up the authorization form that CLI is requesting to use your current login. Once you
  authorize, boom! You can access LocalOps services via CLI

  <img src="https://mintcdn.com/localops/eqO0sOcHPRQksUxy/changelogs/images/2026/cli-device-auth-web.png?fit=max&auto=format&n=eqO0sOcHPRQksUxy&q=85&s=c921ff7ba9697d5a355481d27b59346f" alt="cli device auth" style={{ height: '450px', width: '100%' }} width="1160" height="1040" data-path="changelogs/images/2026/cli-device-auth-web.png" />

  You will need to update the CLI version to v3.0.0 to get this update. Checkout [cli](/cli) docs for installing the
  correct CLI for your OS.
</Update>

<Update label="March 6, 2026">
  ## Custom CIDRs for environments

  While creating new environments, you can now specify custom CIDR block for your environments. This will be used to
  create VPCs and subnets in your cloud accounts and will give more control over your network configurations.

  In the Create environment page, you can click on "Advanced options" to see the new field for custom CIDR block. You can
  specify any valid CIDR block that you want for your environment.

  <img src="https://mintcdn.com/localops/cBUI2CGn17D2TAoV/images/create-env-cidr.png?fit=max&auto=format&n=cBUI2CGn17D2TAoV&q=85&s=f3c08f557171b0cadfa23adaa59931a2" alt="env cidr" style={{ height: '350px', width: 'auto' }} width="1958" height="1798" data-path="images/create-env-cidr.png" />
</Update>

<Update label="March 3, 2026">
  ## Realtime run status of services

  We released a major new enhancement to services today. You can now see the realtime status of all services running
  within each environment, without having to use LocalOps CLI or Kubectl CLI.

  <img src="https://mintcdn.com/localops/oIxizGzDGPIsMnXP/images/run-status.png?fit=max&auto=format&n=oIxizGzDGPIsMnXP&q=85&s=e70d84fcd0b9a3af1884b708dd567c68" alt="service run status" style={{ height: '250px', width: 'auto' }} width="1798" height="810" data-path="images/run-status.png" />

  Depending on the service type, status text will be different. Here are the possible status texts:

  For example, for `web`, `internal` services and `worker` services, you will see the following statuses:

  * running (2/2)
  * degraded (1/2)
  * failing (0/2)
  * stopped

  (x/y) format shows the number of replicas running (x) out of total number of replicas (y).

  For job services:

  * pending
  * succeeded
  * failed
  * timeout

  For cronjob services:

  * idle
  * active
  * suspended

  You can see them in service tile view or within service section at the top of the page.

  ### New "Runs" tab

  Lookout for the new "Runs" tab for job and cronjob services. You can see the run history and status of each run.

  We update statuses in near real time so you would know when you service is failing, just by looking at the status text
  in LocalOps console.

  All of these changes were made to make it easier for you to monitor and manage your services, without having to drop
  into shell or CLI.
</Update>

<Update label="Feb 27, 2026">
  ## New feature: Custom Node groups

  So far, environments have been having a single node group created, to run all the services.

  Now, you can create multiple node groups to run different types of services. For each node group you can pick

  1. AMIs
  2. Instance types
  3. Desired count of nodes

  <img src="https://mintcdn.com/localops/oIxizGzDGPIsMnXP/images/list-node-groups.png?fit=max&auto=format&n=oIxizGzDGPIsMnXP&q=85&s=afb1643444685cba309f5d629414ec93" alt="list node groups" style={{ height: '200px', width: 'auto' }} width="2718" height="864" data-path="images/list-node-groups.png" />

  Go to the new Node groups section to create and manage node groups.

  You can now create

  1. Windows node groups to run windows based services (or)
  2. Linux node groups to run linux base services

  And while creating a service or updating a service, you can assign it to any one of the node groups. And all its
  replicas or containers will run in configured nodes.

  ## Spot instances

  In the case of AWS evnironments, while creating node groups, you can pick between ON\_DEMAND or SPOT as capacity types.

  So you can create node groups of SPOT instance type and assign interruptible and idempotent services like jobs, crons,
  batch processing, or other. This will end up giving up to 50% savings in compute costs.
</Update>

<Update label="Feb 9, 2026">
  ## New feature: Deployment notes

  While triggering new deployments, you can now pass a note text to it. And it will communicate to rest of the team about
  what the deployment is all about.

  We made other QoS updates here to ensure smooth execution of deployment pipelines.
</Update>

<Update label="Feb 7, 2026">
  ## New feature: Account level resource tags

  All cloud resources of all environments spinned by LocalOps are attached with two standard tags. One with ID of the
  environment and another with name of the environment. These tags can be used in Cost explorer to analyse costs at a
  granular level.

  We released a new feature today to accept custom tags so that you can assign your own key value pairs as tags for all
  resources spinned up in all environments of the account. You can use it to attach tags like:

  1. `bu: internal`
  2. `bu: product-1`
  3. `project: project-name`

  Go to Account settings > Resource tags to start adding custom tags.
</Update>

<Update label="Jan 13, 2026">
  ## Bring your own registry

  LocalOps can now use your images from your own docker registry to deploy services in your environments. Go to the new
  Registries section to add any private docker registry.

  We support ECR & DockerHub as of today and plan to support other providers in the coming weeks.

  1. Amazon Elastic Container Registry (ECR)
  2. DockerHub
  3. Google container registry
  4. Azure container registry
  5. Github packages

  ([Talk to us](mailto:anand@localops.co) if you need us to support any other registry).

  ## Bring your code pipeline

  ### Deploy using LocalOps API

  You can run your exisitng code build pipelines to build code and finally call LocalOps API to deploy the code to your
  environment. Checkout the API reference for the new /deploy api
  [here](https://docs.localops.co/api-reference/deploy-service).

  Eg.,

  ```bash theme={null}
  curl --request POST \
    --url https://sdk.localops.co/v1/environments/{envId}/services/{serviceId}/deploy \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --data '
  {
    "docker_image_tag": "a1b2c3d4"
  }
  '
  ```

  ### 🍦 Deploy using LocalOps Github action

  To cut work, you can also embed our Github action step directly within your `.github/workflows/deploy.yml` to trigger
  new deployments.

  Like:

  ```yaml theme={null}
  - use: localopsco/deploy-action
    with:
      - service: auf-d0923-8rljks-fd9o8
      - environment: afdsfk-j092309-4laskd-jf32
      - token: aslkjadsf-dkjfie-uriw-skjf-19823r
      - docker_image_tag: 1.2.2
  ```

  You can pass `docker_image_tag` or `commit_id` or `helm_chart_version` to the action to trigger new deployments.

  or with commit sha like:

  ```yaml theme={null}
  - use: localopsco/deploy-action
    with:
      - service: auf-d0923-8rljks-fd9o8
      - environment: afdsfk-j092309-4laskd-jf32
      - token: aslkjadsf-dkjfie-uriw-skjf-19823r
      - commit_id: akf9a0sd98
  ```

  ## Pass ops.json as configuration

  For Docker-image based services, you can now add ops.json as configuration to the service settings within LocalOps
  console. This will work as documented in ops.json configuration
  [here](https://docs.localops.co/environment/services/ops-json).
</Update>

For the previous year, check out [2025](/changelogs/2025)

[demo]: https://go.localops.co/tour

[signup]: https://console.localops.co/signup


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.