---
title: "Deploy to Cloud Foundry"
description: "Configure CircleCI to deploy to Cloud Foundry with the Cloud Foundry CLI, using a blue-green pattern of dark and live deployments with optional approval."
platform: "Cloud, Server"
doc_version: "Server"
last_updated: "2026-10-06"
---

> For current CircleCI product defaults, deprecated patterns, and Cloud/Server differences, see [AGENTS.md](https://circleci.com/docs/AGENTS.md).
>
> For the complete documentation index and site structure, see [llms.txt](https://circleci.com/docs/llms.txt).

# Deploy to Cloud Foundry

In this how-to guide, you will learn how to configure CircleCI to deploy to Cloud Foundry.

## Introduction

Cloud Foundry deployments require the Cloud Foundry CLI. Be sure to match the architecture to your Docker image (the commands below assume you are using a Debian-based image). This example pattern implements "Blue-Green" deployments using Cloud Foundry’s map-route/unmap-route commands, which is an optional feature above and beyond a basic `cf push`.

### 1\. Install the CLI

```yaml
      - run:
          name: Setup CF CLI
          command: |
            curl -v -L -o cf-cli_amd64.deb "https://cli.run.pivotal.io/stable?release=debian64&source=github"
            sudo dpkg -i cf-cli_amd64.deb
            cf -v
            cf api https://api.run.pivotal.io  # alternately target your private Cloud Foundry deployment
            cf auth "$CF_USER" "$CF_PASSWORD"
            cf target -o "$CF_ORG" -s "$CF_SPACE"
```

### 2\. Dark deployment

This example is the first step in a [Blue-Green](https://docs.cloudfoundry.org/devguide/deploy-apps/blue-green.html) deployment, pushing the application to non-production routes.

```yaml
      - run:
          name: CF Deploy
          command: |
            # push artifacts on "dark" subdomain, and set environment variables before running `cf start`.
            cf push --no-start <app-name-dark> -f manifest.yml -p application.jar -n dark -d <example.com>
            # Pass CircleCI variables to Cloud Foundry (optional)
            cf set-env <app-name-dark> circle_build_num ${CIRCLE_BUILD_NUM}
            cf set-env <app-name-dark> circle_commit ${CIRCLE_SHA1}
            cf set-env <app-name-dark> circle_workflow_guid ${CIRCLE_WORKFLOW_ID}
            cf set-env <app-name-dark> circle_user ${CIRCLE_PROJECT_USERNAME}
            cf set-env <app-name-dark> circle_repo ${CIRCLE_PROJECT_REPONAME}
            # Start the application
            cf start <app-name-dark>
            # Ensure dark route is exclusive to dark app
            cf unmap-route <app-name> <example.com> -n dark || echo "Dark Route Already exclusive"
```

### 3\. Live deployment

Until now, the previously pushed "app-name" has not changed. The final step is to route the production URL to our dark application, stop traffic to the previous version, and rename the applications.

```yaml
      - run:
          name: Re-route live Domain to latest
          command: |
            # Send "real" url to new version
            cf map-route app-name-dark example.com -n www
            # Stop sending traffic to previous version
            cf unmap-route app-name example.com -n www
            # stop previous version
            cf stop app-name
            # delete previous version
            cf delete app-name -f
            # Switch name of "dark" version to claim correct name
            cf rename app-name-dark app-name
```

### 4\. Manual approval (optional)

For additional control or validation, you can add a manual "hold" step between the dark and live steps as shown in the sample workflow below.

```yaml
workflows:
  build-deploy:
    jobs:
      - test
      - dark-deploy:
          requires:
            - test
          filters: pipeline.git.branch == "main" # only run the dark-deploy job on the main branch
      - hold:
          type: approval
          requires:
            - dark-deploy
          filters: pipeline.git.branch == "main" # only run the hold approval job on the main branch
      - live-deploy:
          requires:
            - hold # manual approval required via the CircleCI UI to run the live-deploy job
          filters: pipeline.git.branch == "main" # only run the live-deploy job on the main branch
```