Category: Linux Server

  • ๐Ÿš€ Building a Simple File Upload Server on AWS EC2 with FastAPI (Amazon Linux 2023)

    Learn how to upload photos and videos from your phone directly to an AWS EC2 server using Python and FastAPI.


    ๐Ÿ“Œ Overview

    One of the most common requirements in modern applications is the ability to upload files from a browser to a server.

    Whether you’re building:

    • A media management platform
    • A document portal
    • A machine learning application
    • An image processing workflow
    • A video processing pipeline
    • A backup solution

    The first step is always the same:

    Get the file from the user’s device to your server.

    In this tutorial, we’ll build a lightweight file upload application using:

    • AWS EC2
    • Amazon Linux 2023
    • Python
    • FastAPI
    • HTML
    • JavaScript

    The result is a mobile-friendly web page that allows users to upload photos and videos directly to an EC2 instance.

    Table of Contents


    ๐Ÿ—๏ธ Final Architecture

    Phone / Laptop
           โ”‚
           โ–ผ
    FastAPI Web UI
           โ”‚
           โ–ผ
    AWS EC2 Instance
           โ”‚
           โ–ผ
    uploads/
    

    The workflow is intentionally simple:

    1. User selects a file.
    2. Browser uploads the file.
    3. FastAPI receives the file.
    4. File is saved to disk.

    No database required.

    No external storage required.

    No frontend framework required.


    โ˜๏ธ Step 1: Launch an AWS EC2 Instance

    Launch a new EC2 instance.

    Instance Configuration

    • Amazon Linux 2023
    • t2.nano
    • Public IPv4 Enabled

    Create and download a new key pair.

    Example:

    MediaUploadServer.pem
    

    ๐Ÿ”‘ Step 2: Connect to the Server

    Connect using SSH:

    ssh -i .\MediaUploadServer.pem ec2-user@13.220.113.175
    

    Fixing “Unprotected Private Key” Errors on Windows

    If Windows reports that your PEM file permissions are too open, run:

    icacls .\MediaUploadServer.pem /inheritance:r
    icacls .\MediaUploadServer.pem /grant:r "$($env:USERNAME):(R)"
    icacls .\MediaUploadServer.pem /remove "Authenticated Users" "BUILTIN\Users" "Everyone"
    

    Then reconnect.


    ๐Ÿ”„ Step 3: Update the Server

    After logging in:

    sudo yum update -y
    sudo dnf update -y
    

    Install Python:

    sudo yum install python3 -y
    sudo yum install python3-pip -y
    

    Verify installation:

    python3 --version
    

    ๐Ÿ“ Step 4: Create the Project Directory

    Create a project folder:

    mkdir uploader
    cd uploader
    

    ๐Ÿ Step 5: Create a Python Virtual Environment

    Create a virtual environment:

    python3 -m venv venv
    

    Activate it:

    source venv/bin/activate
    

    Your terminal should now display:

    (venv)
    

    indicating that the virtual environment is active.


    Why Use a Virtual Environment?

    A virtual environment keeps project dependencies isolated from the operating system.

    Without a virtual environment:

    System Python
    โ”œโ”€โ”€ FastAPI
    โ”œโ”€โ”€ Django
    โ”œโ”€โ”€ NumPy
    โ””โ”€โ”€ Other Projects
    

    With a virtual environment:

    uploader/
    โ””โ”€โ”€ venv/
    

    Everything stays self-contained.


    Verify the Virtual Environment

    Before activation:

    which python3
    

    Example:

    /usr/bin/python3
    

    After activation:

    which python
    

    Example:

    /home/ec2-user/uploader/venv/bin/python
    

    This confirms that Python commands are now using the virtual environment.


    Leaving the Virtual Environment

    When finished:

    deactivate
    

    Do You Need to Activate It Every Time?

    For development:

    cd uploader
    source venv/bin/activate
    

    Yes.

    For production services, you can directly reference the virtual environment’s Python executable:

    ExecStart=/home/ec2-user/uploader/venv/bin/uvicorn app:app --host 0.0.0.0 --port 8000
    

    No manual activation required.


    ๐Ÿ“ฆ Step 6: Install Dependencies

    Install FastAPI and related packages:

    pip install fastapi uvicorn python-multipart
    

    Packages:

    FastAPI

    Python web framework.

    Uvicorn

    ASGI web server.

    python-multipart

    Required for file uploads.


    Where Are Packages Installed?

    Because we activated the virtual environment, packages are installed into:

    uploader/venv/lib/python3.x/site-packages/
    

    instead of:

    /usr/lib/python3.x/
    

    Save Dependencies

    Generate a requirements file:

    pip freeze > requirements.txt
    

    Project structure:

    uploader/
    โ”œโ”€โ”€ app.py
    โ”œโ”€โ”€ requirements.txt
    โ”œโ”€โ”€ uploads/
    โ”œโ”€โ”€ static/
    โ””โ”€โ”€ venv/
    

    ๐Ÿ“‚ Step 7: Create Required Directories

    Create storage folders:

    mkdir uploads
    mkdir static
    

    โš™๏ธ Step 8: Build the Backend

    Create:

    nano app.py
    

    Paste:

    from fastapi import FastAPI, UploadFile, File
    from fastapi.responses import FileResponse
    import os
    
    app = FastAPI()
    
    UPLOAD_DIR = "uploads"
    os.makedirs(UPLOAD_DIR, exist_ok=True)
    
    @app.get("/")
    def home():
        return FileResponse("static/index.html")
    
    @app.post("/upload")
    async def upload(file: UploadFile = File(...)):
        filepath = os.path.join(UPLOAD_DIR, file.filename)
    
        with open(filepath, "wb") as f:
            f.write(await file.read())
    
        return {
            "success": True,
            "filename": file.filename
        }
    

    ๐Ÿ“ Nano Editor Tips

    To save:

    CTRL + O
    Enter
    

    To exit:

    CTRL + X
    

    Save and exit sequence:

    CTRL + O
    Enter
    CTRL + X
    

    ๐ŸŽจ Step 9: Build the Frontend

    Create:

    nano static/index.html
    

    Paste:

    <!DOCTYPE html>
    <html>
    <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Uploader</title>
    </head>
    <body>
    
    <h2>Upload Photo or Video</h2>
    
    <input type="file" id="file">
    
    <br><br>
    
    <button onclick="uploadFile()">
    Upload
    </button>
    
    <p id="status"></p>
    
    <script>
    async function uploadFile() {
        const file = document.getElementById("file").files[0];
    
        if (!file) {
            alert("Select a file");
            return;
        }
    
        const formData = new FormData();
        formData.append("file", file);
    
        document.getElementById("status").innerText = "Uploading...";
    
        const response = await fetch("/upload", {
            method: "POST",
            body: formData
        });
    
        const result = await response.json();
    
        document.getElementById("status").innerText =
            "Uploaded: " + result.filename;
    }
    </script>
    
    </body>
    </html>
    

    ๐Ÿš€ Step 10: Start the Application

    Run:

    uvicorn app:app --host 0.0.0.0 --port 8000
    

    Expected output:

    Uvicorn running on http://0.0.0.0:8000
    

    ๐Ÿ”ฅ Step 11: Configure AWS Security Groups

    This step is critical.

    Open the EC2 Security Group.

    Add:

    Type: Custom TCP
    Port: 8000
    Source: 0.0.0.0/0
    

    ๐Ÿ› Real-World Debugging Lesson

    The application initially failed to load from the browser.

    Everything appeared correct:

    โœ… FastAPI running

    โœ… Uvicorn listening

    โœ… Public IP assigned

    โœ… Route Table configured

    โœ… Network ACL configured

    Yet the browser timed out.

    After investigating, the issue turned out to be:

    Security Group Port: 8080
    Application Port:   8000
    

    The Security Group was opening port 8080 while FastAPI was running on port 8000.

    Once port 8000 was added to the Security Group, the application became accessible immediately.

    A great reminder that networking issues are often caused by small configuration mismatches.


    ๐Ÿ“ฑ Testing the Application

    Open:

    http://YOUR_PUBLIC_IP:8000
    

    From:

    • Android
    • iPhone
    • Laptop
    • Tablet

    Select a photo or video.

    Press Upload.

    Success!


    ๐Ÿ“‚ Verify Uploaded Files

    On the server:

    ls -lh uploads
    

    Example:

    reel+aud.mp4
    

    The uploaded file is now stored on the EC2 instance.


    ๐Ÿ“ธ Suggested Screenshots

    Add screenshots for:

    AWS EC2 Instance

    [Insert Screenshot]

    SSH Connection

    [Insert Screenshot]

    Virtual Environment

    [Insert Screenshot]

    FastAPI Running

    [Insert Screenshot]

    Security Group Configuration

    [Insert Screenshot]

    Upload Page

    [Insert Screenshot]

    Successful Upload

    [Insert Screenshot]


    ๐Ÿ”— Source Code

    GitHub Repository:

    [Insert GitHub Link]


    ๐Ÿš€ Next Improvements

    Once the upload pipeline is working, you can extend it in many ways:

    • Upload progress bars
    • Multiple file uploads
    • Drag-and-drop support
    • Authentication
    • User accounts
    • HTTPS with Nginx
    • Domain names
    • Cloud storage integration
    • Image optimization
    • Video processing
    • AI-powered file analysis

    The possibilities are endless.


    Final Thoughts

    Building a file upload server doesn’t need to be complicated.

    With a small EC2 instance, FastAPI, and a few lines of HTML and JavaScript, it’s possible to create a functional upload system in less than an hour.

    The final result is a lightweight, mobile-friendly upload service that can serve as the foundation for countless applications and workflows.

    Sometimes the best approach is to start with the simplest working version and build from there.