> For the complete documentation index, see [llms.txt](https://docs.release.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.release.com/guides-and-examples/common-setup-examples/flask-rds.md).

# Flask and RDS counter app

Let's walk through an end-to-end example of building a basic counting app using:

* Flask
* PostgreSQL
* RDS (AWS Relational Database Service)

We'll deploy the Flask application into our Release cluster and configure a security group to allow the nodes in the cluster to communicate with the RDS database. You can use a similar method to configure access to other cloud services, such as Redshift, Elasticache, or Redis.

Our final setup will look like this:

![Counter app and RDS in AWS VPC](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-e3f3bbb673ea27a2e0b38f07eba1af4cee4974f6%2Fflask-rds-overview-diagram.png?alt=media\&token=df2b3c39-42bc-48d4-b898-38a21533e98c)

Note how the database and node group exist in the same VPC and use a common security group to communicate.

To follow along, you should have a Release account configured with AWS, with a domain name and cluster already running. Follow the [quickstart guide](/getting-started/quickstart.md) if you don't have these in place yet.

## Set up an RDS instance

We'll create a small, free-tier RDS instance for this demonstration, but for a production application, you'll want to tweak the settings regarding the number of instances, multi-region deployments, backups, and so on. If you already have an RDS database, you'll just need to configure it to:

* Share a VPC with your Release node group.
* Share a security group with your Release node group.

### Identify the security group of your node group in AWS

When you set up a new cluster and node group in Release, an EC2 launch template was automatically created for you with a default security group. We'll need to find this security group that your worker nodes already use so we can assign it to the database.

Log in to your Release account, and take note of your cluster context and node group name, as both of these are used in the name of the security group. You can see how to find each in the image below.

![Find the cluster identifier and node group name](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-6fb91eee5712fa17649377f08f7a79276571f54e%2Fflask-rds-cluster-nodegroup-name.png?alt=media)

Also note which **region** your cluster is running in.

Now log in to your AWS account and navigate to the EC2 dashboard for the same region as your Release cluster. You should see at least one (and probably more) EC2 instances that are worker nodes for your cluster. You can identify them as they have the same cluster context and node group name that we noted in the Release control panel in their name, followed by the suffix `-Node`.

Select one of these worker nodes and navigate to **Actions** -> **Security** -> **Change Security Groups**, as shown below.

![Navigate to security groups for EC2 instance](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-f511969e17baafb311de11af8373a5c954331719%2Fflask-rds-get-security-group-ec2.png?alt=media)

Don't make any changes here, but note the name of the security group and then press **Cancel**.

![Note the security group name](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-0e6955080eca8608971523f53c83ce37754592a2%2Fflask-rds-security-group-name.png?alt=media\&token=d32bd7ef-1a33-417d-bdad-766353e1703f)

Finally, back on the main EC2 page, note the VPC ID of the worker node too. You can find it as shown in the image below.

![Get the VPC ID of an EC2 instance](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-527a7e76ecac864cfe12552cc11b96496937c6b1%2Fflask-rds-ec2-vpc-id.png?alt=media\&token=0b1b0f21-1f13-41cd-a96a-ff27d2fd4676)

### Create the RDS database

Now navigate to the RDS dashboard and choose to create a new database.

![Create a new RDS database](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-812394d6c6e2c249c58b1feb0e2933c8715bad37%2Fflask-rds-create-database.png?alt=media\&token=b0e97567-9f6e-46c6-bfcb-66c0667d9c98)

The most important settings to change are:

* The VPC - Make sure you create the database in the same VPC as your Release clusters.
* The security group - Add the same security group as the one you noted earlier.

For this demo, we left most of the default settings for a free-tier Postgres database, as detailed below.

Leave the default database creation method as **Standard create** and choose **PostgreSQL** as the database type.

![Choose Postgres](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-4f42834ddd5682bbd31ab3d529becf4daea773ea%2Fflask-rds-create-database-choose-postgres.png?alt=media\&token=eb72bac0-fcc7-44ef-90c5-2a691854c6b7)

Choose **Free tier**, name your database something meaningful, and choose a strong password (note this somewhere safe).

![Choose free tier and set name and password](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-1c3267c62c96349c197a5032137b383dc4f62fd5%2Fflask-rds-create-database-free-tier-password.png?alt=media\&token=2032f279-8a3f-44fa-ab2b-f283dced1323)

Set the VPC and security group to match the settings from your Release EC2 worker node, which we noted in the previous section.

![Set VPC and security group](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-9ceda8cea1d42d1aefda3e69144d9707d1a91d4f%2Fflask-rds-create-database-vpc-security-group.png?alt=media\&token=1a910b84-3562-4cae-b8a6-09c7715c6f09)

Scroll to the bottom and click **Create database**.

![Confirm create](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-f9be687ff82aaf1a783df461e5948d53ad06946d%2Fflask-rds-create-database-confirm-create.png?alt=media\&token=28e2eca4-f9ab-4354-a596-728447b2c8b7)

The database will take a few minutes to initialize. Once it's ready, navigate to it from the RDS dashboard and note the endpoint, as shown below.

![Note RDS endpoint](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-64a057494c5606c4c603f2a9e148cb738284cf47%2Fflask-rds-get-rds-endpoint.png?alt=media\&token=94e46d74-b06c-4060-9578-26cdff5e2648)

You now have an RDS database that your Release cluster can communicate with. To complete the example, we'll build a basic Flask application that uses this database and deploy it into our cluster.

## Build the application

For the demo, we'll build a very basic "counter" application that counts the number of times the **Count** button has been pressed. It'll look like this when it's up and running:

![An application showing a count button and the total recorded counts](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-73f2dfa6825873c9bb1a0d8ac3b3c238b8ded49c%2Fflask-rds-counter-result.png?alt=media\&token=99255cf3-992a-477b-aa8d-a3e9c076ee17)

Our full project will have the following directory structure. We'll use a Docker Compose file even though we're only running a single container to make it easier to extend the app. We've called our app `count`, and placed all the application code in an `app.py` file and the frontend code in `templates/index.html`.

![Directory structure as tree output](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-2f86bda099b44dd1c3df2b837c2e6109b7fb47be%2Fflask-rds-directory-structure.png?alt=media\&token=4033b03b-962e-4d40-9afe-dc67e1d5f1a8)

Create a directory for the overall project, a subdirectory called `count` for the Python application, and a subdirectory in the `count` directory called `templates` for the frontend. In the `count` directory, create blank files for `app.py`, `Dockerfile`, and `requirements.txt`. In the templates directory, create a blank file called `index.html`. In the outer project directory, create a blank file called `docker-compose.yml`.

### Write the `app.py` file

Our `app.py` file uses Flask-SQLAlchemy as an ORM. First we get credentials from our environment (we'll set these up later), and then we define a basic `counts` object, which has an automatic primary key and a single field that always stores `1`.

We then initialize the database and define a `hello()` method. When a user visits the page, we'll count the number of records in the database. If the user presses the **Count** button, we'll create a new count object, which will have the effect of incrementing the total.

Finally, we run the application on port 80 with debug turned on.

Add the following code to the `count/app.py` file:

```python
from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
import traceback
import os

user = os.environ.get("POSTGRES_USER")
pw = os.environ.get("POSTGRES_PASSWORD")
host = os.environ.get("POSTGRES_HOST")
db = os.environ.get("POSTGRES_DATABASE_NAME")
DB_URL = f"postgresql+psycopg2://{user}:{pw}@{host}/{db}"
print(DB_URL)

app = Flask(__name__)

app.config["SQLALCHEMY_DATABASE_URI"] = DB_URL
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

db = SQLAlchemy(app)


class counts(db.Model):
    id = db.Column("count_id", db.Integer, primary_key=True)
    value = db.Column(db.Integer)


try:
    db.create_all()
except Exception as e:
    print(e)
    print("couldn't initialize db")
    traceback.print_exc()


@app.route("/", methods=["POST", "GET"])
def hello():
    if request.method == "POST":
        c = request.form["countbutton"]
        count = counts(value=1)
        db.session.add(count)
        db.session.commit()

    total_counts = counts.query.count()
    return render_template("index.html", total=total_counts)


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=80, debug=True, threaded=True)
```

Note that we've taken some liberties here in terms of best practices to keep the code simple. Specifically, for production settings, you should:

* Turn debug mode off in the last line.
* Run `db.create_all()` in a separate once-off script rather than in the web application code.
* Properly handle the exception rather than only logging the error if the database connection fails.

### Write the `templates/index.html` file

Our frontend contains a basic form with a single button and a templated variable `{{count}}` to display the total. Add the following code to `templates/index.html`:

```html
<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
        <h3>Count</h3>
        <p>Total counts: {{total}}</p>
        <form id="countform" name='form' method="POST" action="/">
          <input id="countbutton" type="submit" name="countbutton" value="Count"></input>
        </form>
  </body>
</html>
```

### Write the Dockerfile

For our Dockerfile, we'll extend the Python3 Alpine Docker image, as this has most of the packages we need while still being very slim. Add the following code to the `count/Dockerfile` file:

```docker
# Using official Python runtime base image
FROM python:3-alpine

# Set the application directory
WORKDIR /app

# Install our requirements.txt
ADD requirements.txt /app/requirements.txt

RUN apk add build-base

RUN apk add --no-cache supervisor \
    && python -m pip install --upgrade pip \
    && pip install -r requirements.txt

# Copy our code from the current folder to /app inside the container
ADD . /app

# Make port 80 available for links and/or publish
EXPOSE 80

# Define our command to be run when launching the container
CMD ["gunicorn", "app:app", "-b", "0.0.0.0:80", "--log-file", "-", "--access-logfile", "-", "--workers", "4", "--keep-alive", "0"]
```

This brings in a few more dependencies that we need to correctly install the `psycopg2binary` database driver, copies in our application code, installs our Python requirements, and runs the application using Gunicorn.

### Write the `requirements.txt` file

Our application needs Flask (our web framework), Gunicorn (to act as a gateway between Flask and our webserver), Flask-SQLAlchemy (as an ORM to easily create objects in our database), and `psycopg2-binary` (as a driver to talk to PostgreSQL).

```bash
Flask
gunicorn
flask-sqlalchemy
psycopg2-binary
```

### Write the `docker-compose.yaml` file

Finally, we'll initialize everything using Docker Compose. This step is not essential in our case as we have only a single Docker file, but real-world applications will usually tie together a few different containers so it's useful to have it in place. Add the following code to the top-level `docker-compose.yml` file:

```
version: "1"

services:
  count:
    build: ./count
    command: 
    - python 
    - app.py
    ports:
      - "5000:80"
```

This creates a single service (`count`) and builds the Docker file in our `count` directory.

We now have a full-stack application running. If you have Docker and Postgres running locally, you can set the expected environment variables and run `docker-compose up` to test that everything is working, otherwise we can skip straight to deploying it on Release and debugging any issues there.

## Deploy the application to Release

To deploy the application to Release, you need to:

* Push the application up to GitHub or other provider that you've integrated with Release.
* Create a new application in Release.
* Configure the environment variables to connect to the database.

### Create the application

You can find detailed steps for doing this in our [create your application guide](https://github.com/releasehub-com/docs/blob/main/examples/broken-reference/README.md), but we'll cover the most important steps below.

In your Release dashboard, choose to create a new application.

![Create new app](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-021ac5caf31473958989a19850820cd8d478ef94%2Fflask-rds-create-app.png?alt=media\&token=9b3132ea-c285-4de7-9a22-a71b1cc18619)

Select the repository that holds your application code and proceed to the next step. You can leave all the defaults in the generated Application Template.

In the "Build & Runtime Configurations" section, choose to **Edit** "Default Environment Variables", and add in the connection settings for the database.

![Edit environment variables](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-8233bc0a7ab081fbdd22df220e6fd621124a31bd%2Fflask-rds-edit-env-variables.png?alt=media\&token=a2076b9d-8505-4f3d-804e-4651a36ab299)

The bottom of the file (after the comments) should look as follows (substituting in your own database endpoint and password that you set in AWS, and username and database name if you changed those from their defaults).

```yaml
defaults: []
services:
  count:
  - key: POSTGRES_DATABASE_NAME
    value: postgres
  - key: POSTGRES_USER
    value: postgres
  - key: POSTGRES_PASSWORD
    secret: true
  - key: POSTGRES_HOST
    secret: true
```

Note how we define these under the `count` service (named in our `docker-compose.yml` file) so that these are only available to our main application service. We've also set the `secret` flag on the password and host fields to `true`, so these will be encrypted by Release at rest.

Click **Start Build & Deploy** and wait for the environment to come up. Once it's ready, you can click the URL in the section "Hosted URLs" to navigate to your running application.

![View hosted URL](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-d86738b68bbc40a0b1ab16877bf06a6d52b92a06%2Fflask-rds-hosted-url.png?alt=media\&token=752b7591-829f-493a-af35-1643417ff547)

If everything worked as expected, you should see the basic counting application running and be able to add to the count by clicking on the button.

![Final app](https://585411240-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M1neGLLQ0sDXeK6ooSo%2Fuploads%2Fgit-blob-73f2dfa6825873c9bb1a0d8ac3b3c238b8ded49c%2Fflask-rds-counter-result.png?alt=media\&token=99255cf3-992a-477b-aa8d-a3e9c076ee17)

If you see an error or a blank page, [use the log viewer](https://github.com/releasehub-com/docs/blob/main/guides-and-examples/running-instances/viewing-logs.md) to look for any errors, or [use the terminal](https://github.com/releasehub-com/docs/blob/main/guides-and-examples/running-instances/using-terminal.md) to double check that the environment variables are set correctly by running, for example, `echo $POSTGRES_USER`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.release.com/guides-and-examples/common-setup-examples/flask-rds.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
