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

# Writing your first deployment

export const Comments = ({children}) => {
  return <div class="my-4 px-5 py-4 overflow-hidden rounded-2xl flex gap-3 border border-zinc-500/20 bg-zinc-50/50 dark:border-zinc-500/30 dark:bg-zinc-500/10" data-callout-type="comments">
      <div class="w-4">
        <svg width="14" height="14" viewBox="0 0 640 640" fill="currentColor" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" aria-label="Comments">
            <path d="M320 112C434.9 112 528 205.1 528 320C528 434.9 434.9 528 320 528C205.1 528 112 434.9 112 320C112 205.1 205.1 112 320 112zM320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM280 400C266.7 400 256 410.7 256 424C256 437.3 266.7 448 280 448L360 448C373.3 448 384 437.3 384 424C384 410.7 373.3 400 360 400L352 400L352 312C352 298.7 341.3 288 328 288L280 288C266.7 288 256 298.7 256 312C256 325.3 266.7 336 280 336L304 336L304 400L280 400zM320 256C337.7 256 352 241.7 352 224C352 206.3 337.7 192 320 192C302.3 192 288 206.3 288 224C288 241.7 302.3 256 320 256z" />
        </svg>
      </div>
      <div class="text-sm prose min-w-0 w-full">
        {children}
      </div>
    </div>;
};

This tutorial walks you through deploying a long-running service on Anaconda Platform: a minimal Flask app packaged with its dependencies, configured with a YAML file, and launched with the CLI. To deploy a model from the model catalog instead, see [Deploy a model from the model catalog](/docs/platform/guides/inference/deploy-model-from-catalog).

## A "hello world" deployment

For this tutorial, assume you have a simple Flask service:

```text theme={null}
my-first-project/
└── first-service.py
```

The code for `first-service.py`:

```python expandable theme={null}
from flask import Flask
import os
import time

app = Flask(__name__)


@app.route("/")
def hello_world():
    return {"message": "Hello, World!"}


if __name__ == "__main__":
    port = int(os.environ.get("PORT", 8000))
    app.run(host="0.0.0.0", port=port)
```

## Managing requirements

The platform automatically packages your dependencies into a Docker container to run your deployment, just like Metaflow tasks.

You can declare dependencies in a few different formats. One of the easiest is a `requirements.txt` file at the root of your project. For all the ways to manage dependencies, see [Dependency management](/docs/platform/guides/inference/deployments-deep-dive#dependency-management).

For this example, create a `requirements.txt` that specifies the one dependency, `flask`:

```text theme={null}
my-first-project/
└── first-service.py
└── requirements.txt
```

## Defining a config file

You can configure the behavior of your deployment either by passing options to the CLI or by creating a config file. Anaconda recommends defining as much configuration as possible in the config file, and using CLI options occasionally to override the config file or for rapid prototyping.

Create a config file called `config.yaml`:

```yaml theme={null}
name: hello-world 
port: 8000
auth:
  type: API

commands: 
- python first-service.py
```

Place it in the root of your project:

```text theme={null}
my-first-project/
└── first-service.py
└── requirements.txt
└── config.yaml
```

## Deploying your service

With the service code and dependencies ready, deploy the service:

```sh theme={null}
outerbounds app deploy --config-file config.yaml
```

The command packages your code, builds a container image with your dependencies, provisions the workers, and exposes the service. The output ends with the URL of your deployed endpoint:

```text expandable theme={null}
     🚀 Deploying hello-world to the platform...
     📦 Packaging directories: my-first-project/
     📦 Using dependencies from requirements.txt: my-first-project/requirements.txt
     🍳 Baking [hello-world] ...
          🐍 Python: 3.9.12
          📦 PyPI packages:
             🔧 flask: 3.0.2
     🏁 Baked [hello-world] in 44.05 seconds!
     🚀 Upgrading endpoint `hello-world`...
     ⏳ 1 new worker(s) pending
     🚀 1 worker(s) started running
     ✅ First worker came online
     🎉 All workers are now running
     💊 Endpoint hello-world is ready to serve traffic on the URL: https://api-c-3si29v.example.com
```

### Understanding the deploy command

<AccordionGroup>
  <Accordion title="Full breakdown of the deploy command and config file">
    Command options:

    * `app deploy` deploys a new deployment or updates an existing one.
    * `--config-file` defines the location of the config file that contains the configuration for your deployment.

    Config file fields:

    * `name` is the globally unique identifier of your deployment. No two deployments can have the same name.
    * `port` is the port number where your service listens. In the Flask example above, the server starts on port 8000, so the config passes the same port.
    * `auth.type` takes two values:
      * `API`: token-based authentication for programmatic clients such as cURL or Python scripts. See [Accessing your deployed endpoint](#accessing-your-deployed-endpoint) for an example.
      * `Browser`: the endpoint uses the same SSO authentication as the platform. Anyone signed in to the platform can access it.
    * `commands` is the command used to launch your service: the same command you would use to run the service locally. In this example, `python first-service.py`. You can provide multiple commands if needed.
  </Accordion>
</AccordionGroup>

## Accessing your deployed endpoint

You can construct a request for your endpoint with cURL, Python, or the language of your choice. The only additional requirement is attaching auth headers to your request so the platform can authenticate it.

If you have `metaflow` in your environment already, use the following snippet to generate the headers for your call:

```python theme={null}
def get_auth_headers():
    from metaflow.metaflow_config_funcs import init_config
    conf = init_config()
    if conf:
        headers = {'x-api-key': conf['METAFLOW_SERVICE_AUTH_KEY']}
    else:
        headers = json.loads(os.environ['METAFLOW_SERVICE_HEADERS'])
    return headers
```

If you are calling the endpoint from an external environment that does not have `metaflow` installed, use the lightweight `ob-auth` package instead:

```sh theme={null}
pip install ob-auth
```

```python theme={null}
def get_auth_headers(): 
    from ob_auth import OuterboundsAuth
    auth = OuterboundsAuth()
    auth.init_from_config()
    return auth.api_headers()
```

Use either function to call your deployed endpoint, replacing the URL with the URL of your deployment:

```python theme={null}
import requests
import os

url = "<ENDPOINT_URL>"
print(requests.get(url, headers=get_auth_headers()).text)
```

<Comments>
  Replace \<ENDPOINT\_URL> with the URL printed at the end of the deploy command output, or the "available at" URL shown in the deployment details view.
</Comments>

The `get_auth_headers()` function works in all of the following cases:

* Running locally from a script when you have a Metaflow config.
* Running from inside a local or remote Metaflow task.
* Running from any environment where you have a machine user configured. If you use `ob-auth` with an IAM machine user, install it with `pip install ob-auth[aws]`.

## Up next

* For a deeper understanding of working with deployments, see [Deployments deep dive](/docs/platform/guides/inference/deployments-deep-dive).
* To manage deployments from the command line, see the [Deployments CLI reference](/docs/platform/cli/app/deploy).
