From Local to Live: Deploying an Audio Transcription & Translation App Using Streamlit, Docker, and GitHub Actions

๐Ÿง  Introduction

In this blog post, Iโ€™ll walk you through how I built, containerized, and deployed a real-time audio transcription and translation app using:

  • ๐ŸŽง Streamlit for the frontend

  • ๐Ÿค– OpenAI Whisper API for transcription

  • ๐ŸŒ Translation API for multilingual output

  • โ˜๏ธ AWS S3 and EC2 for cloud deployment

  • ๐Ÿณ Docker for containerization

  • ๐Ÿ”„ GitHub Actions for CI/CD automation

Whether you're a Python developer, DevOps learner, or looking to automate your deployments โ€” this article is packed with practical takeaways! ๐Ÿš€


๐Ÿ“Œ Project Overview

GitHub Repository: S3-Audio-Transcription-Translation-App

This Streamlit app allows users to:

  • Upload audio files (MP3/WAV)

  • Transcribe audio using OpenAIโ€™s Whisper

  • Translate the transcript into multiple languages

  • Store metadata in MySQL

  • Upload audio files to AWS S3


๐Ÿ› ๏ธ Technologies Used

CategoryTools/Services
FrontendStreamlit
BackendPython, OpenAI API, Translation API
StorageAWS S3
DatabaseMySQL
DevOpsDocker, GitHub Actions, EC2
TestingPytest

๐Ÿšง Step-by-Step Development

1๏ธโƒฃ Build the Streamlit App

I used Streamlit to build a clean UI that takes in audio files, triggers transcription, and displays translated text. Here's a snippet:

import streamlit as st

st.title("Audio Transcription & Translation App")
uploaded_file = st.file_uploader("Upload an audio file", type=["mp3", "wav"])

if uploaded_file:
    st.audio(uploaded_file)
    # Process transcription and translation...

2๏ธโƒฃ Integrate OpenAI & Translation APIs

Once the file is uploaded, the backend sends it to OpenAIโ€™s Whisper API and returns a transcript. This is then sent to a translation API (e.g., Google Translate or OpenAI GPT) based on user-selected language.


3๏ธโƒฃ Containerize with Docker

To ensure consistency across development and production, I created a Dockerfile:

# Use a slim Python base image
FROM python:3.9-slim

RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    portaudio19-dev \
    libasound-dev \
    # Add other system dependencies as needed, e.g., libgfortran5 for numpy
    --no-install-recommends && \
    apt-get clean && rm -rf /var/lib/apt/lists/*

# Set the working directory
WORKDIR /app

# Copy the requirements file and install dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt

# Copy the Streamlit application code
COPY . .

# Expose the default Streamlit port
EXPOSE 8501
# Command to run the Streamlit app with environment variables
# The actual environment variables will be passed during `docker run` on     EC2
CMD ["streamlit", "run", "appfouthjuly.py", "--server.port=8501", "--server.address=0.0.0.0"]

4๏ธโƒฃ Automate with GitHub Actions

Every push to main triggers a GitHub Actions pipeline that:

  • โœ… Runs unit tests using pytest

  • ๐Ÿณ Builds Docker image

  • ๐Ÿ“ค Pushes it to Docker Hub

  • ๐Ÿš€ Deploys it to an EC2 instance via SSH

name: CI/CD Pipeline for Streamlit App to EC2 (Docker Hub)

on:
  push:
    branches:
      - main  # or your desired branch

jobs:
  build-and-deploy:
    runs-on: self-hosted

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx # Add this step to set up Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Run Unit Tests
        run: |
          pytest tests/ --maxfail=1 --disable-warnings --tb=short

      - name: Build and push Docker image to Docker Hub
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}:latest

      - name: Deploy to EC2
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.EC2_PUBLIC_DNS }}
          username: ${{ secrets.EC2_USERNAME }}
          key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
          script: |
            echo "Pulling latest image..."
            sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}:latest

            echo "Stopping/removing existing container if it exists..."
            if [ "$(sudo docker ps -aq -f name=my-streamlit-app)" ]; then
                sudo docker stop my-streamlit-app || true
                sudo docker rm my-streamlit-app || true
            fi

            echo "Checking if any other container is publishing port 8501..."
            if [ "$(sudo docker ps -q -f publish=8501)" ]; then
                CONTAINER_ID_USING_PORT=$(sudo docker ps -q -f publish=8501)
                echo "Stopping and removing container $CONTAINER_ID_USING_PORT using port 8501..."
                sudo docker stop $CONTAINER_ID_USING_PORT || true
                sudo docker rm $CONTAINER_ID_USING_PORT || true
            fi

            echo "Forcefully killing any process on port 8501 (just in case)..."
            sudo fuser -k 8501/tcp || true

            echo "Running new container..."
            sudo docker run -d \
              --network host \
              --name my-streamlit-app \
              -e MYSQL_HOST="127.0.0.1" \
              -e MYSQL_USER="${{ secrets.MYSQL_USERNAME }}" \
              -e MYSQL_PASSWORD="${{ secrets.MYSQL_PASSWORD }}" \
              -e MYSQL_DATABASE="${{ secrets.MYSQL_DATABASE_NAME }}" \
              -e OPENAI_API_KEY="${{ secrets.OPENAI_API_KEY }}" \
              -e S3_BUCKET_NAME="${{ secrets.S3_BUCKET_NAME }}" \
              -e AWS_ACCESS_KEY_ID="${{ secrets.AWS_ACCESS_KEY_ID }}" \
              -e AWS_SECRET_ACCESS_KEY="${{ secrets.AWS_SECRET_ACCESS_KEY }}" \
              -e AWS_REGION="${{ secrets.AWS_REGION }}" \
              ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}:latest

5๏ธโƒฃ Deploy to EC2

Using appleboy/ssh-action, the EC2 server pulls the latest image, removes existing containers (if any), and runs the app on port 8501.

 - name: Deploy to EC2
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.EC2_PUBLIC_DNS }}
          username: ${{ secrets.EC2_USERNAME }}
          key: ${{ secrets.EC2_SSH_PRIVATE_KEY }}
          script: |
            echo "Pulling latest image..."
            sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}:latest

            echo "Stopping/removing existing container if it exists..."
            if [ "$(sudo docker ps -aq -f name=my-streamlit-app)" ]; then
                sudo docker stop my-streamlit-app || true
                sudo docker rm my-streamlit-app || true
            fi

            echo "Checking if any other container is publishing port 8501..."
            if [ "$(sudo docker ps -q -f publish=8501)" ]; then
                CONTAINER_ID_USING_PORT=$(sudo docker ps -q -f publish=8501)
                echo "Stopping and removing container $CONTAINER_ID_USING_PORT using port 8501..."
                sudo docker stop $CONTAINER_ID_USING_PORT || true
                sudo docker rm $CONTAINER_ID_USING_PORT || true
            fi

            echo "Forcefully killing any process on port 8501 (just in case)..."
            sudo fuser -k 8501/tcp || true

๐Ÿ” Environment Variables

Secrets were handled using GitHub Secrets for:

  • AWS keys

  • OpenAI API key

  • MySQL credentials

  • S3 bucket info


๐Ÿงช Testing with Pytest

Before deploying, pytest is used to validate core functionality (e.g., upload, API call mocks):

- name: Run Unit Tests
        run: |
          pytest tests/ --maxfail=1 --disable-warnings --tb=short

โœ… Final Outcome

The app is fully functional and accessible via browser after deployment. On every push to main, changes are automatically tested, built, and deployed โ€” zero manual steps!


๐Ÿ’ก What I Learned

  • Writing production-ready Streamlit apps

  • Docker fundamentals and image optimization

  • CI/CD with GitHub Actions

  • EC2 and SSH deployment strategies

  • Managing secrets and environment variables securely



๐Ÿ™Œ Conclusion

This was a rewarding full-stack + DevOps project that touched frontend, backend, cloud, and automation. Iโ€™ll continue improving the app by adding:

  • ๐ŸŽ™ Real-time microphone support

  • ๐Ÿ“ˆ Logging and monitoring with Prometheus/Grafana

  • ๐Ÿ” JWT Auth for user access control

Thanks for reading! Feel free to fork the repo, try the app, and connect with me if you have questions or ideas.


๐Ÿ“ฌ Let's Connect!

0
Subscribe to my newsletter

Read articles from Krishnat Ramchandra Hogale directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Krishnat Ramchandra Hogale
Krishnat Ramchandra Hogale

Hi! Iโ€™m Krishnat, a Senior IT Associate specializing in Performance Engineering at NTT DATA SERVICES. With experience in cloud technologies, DevOps, and automation testing, I focus on optimizing CI/CD pipelines and enhancing infrastructure management. Currently, I'm expanding my expertise in DevOps and AWS Solutions Architecture, aiming to implement robust, scalable solutions that streamline deployment and operational workflows.