Overview

This deployment includes:

  • EC2 Instance: ARM64-based t4g.small instance running Ubuntu 22.04
  • EBS Storage: 10GB persistent volume for application data
  • Built-in Authentication: Uses Oxy’s native authentication with email/password
  • Direct Access: No load balancer required, direct access to EC2 instance

Prerequisites

Before starting, ensure you have:

  • AWS CLI configured with appropriate permissions
  • SSH key pair created in AWS (or create one during setup)
  • Basic familiarity with AWS Console and CLI

Step 1: Network Infrastructure Setup

1.1 Create VPC and Subnets

# Create VPC
VPC_ID=$(aws ec2 create-vpc \
  --cidr-block 10.0.0.0/16 \
  --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=oxy-simple-vpc}]' \
  --query 'Vpc.VpcId' --output text)

# Enable DNS hostnames
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-hostnames

# Create Internet Gateway
IGW_ID=$(aws ec2 create-internet-gateway \
  --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=oxy-simple-igw}]' \
  --query 'InternetGateway.InternetGatewayId' --output text)

# Attach Internet Gateway to VPC
aws ec2 attach-internet-gateway --internet-gateway-id $IGW_ID --vpc-id $VPC_ID

# Create public subnet
SUBNET_ID=$(aws ec2 create-subnet \
  --vpc-id $VPC_ID \
  --cidr-block 10.0.1.0/24 \
  --availability-zone us-west-2a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=oxy-simple-public-subnet}]' \
  --query 'Subnet.SubnetId' --output text)

# Enable auto-assign public IP
aws ec2 modify-subnet-attribute --subnet-id $SUBNET_ID --map-public-ip-on-launch

# Create route table
ROUTE_TABLE_ID=$(aws ec2 create-route-table \
  --vpc-id $VPC_ID \
  --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=oxy-simple-public-rt}]' \
  --query 'RouteTable.RouteTableId' --output text)

# Add route to Internet Gateway
aws ec2 create-route --route-table-id $ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $IGW_ID

# Associate route table with subnet
aws ec2 associate-route-table --subnet-id $SUBNET_ID --route-table-id $ROUTE_TABLE_ID

1.2 Create Security Group

# Create security group for EC2 instance
EC2_SG_ID=$(aws ec2 create-security-group \
  --group-name oxy-simple-sg \
  --description "Security group for Oxy EC2 instance with built-in auth" \
  --vpc-id $VPC_ID \
  --tag-specifications 'ResourceType=security-group,Tags=[{Key=Name,Value=oxy-simple-sg}]' \
  --query 'GroupId' --output text)

# Allow SSH access (port 22)
aws ec2 authorize-security-group-ingress \
  --group-id $EC2_SG_ID \
  --protocol tcp \
  --port 22 \
  --cidr 0.0.0.0/0

# Allow HTTP access (port 3000 for Oxy)
aws ec2 authorize-security-group-ingress \
  --group-id $EC2_SG_ID \
  --protocol tcp \
  --port 3000 \
  --cidr 0.0.0.0/0

# Allow MCP SSE access (port 8000)
aws ec2 authorize-security-group-ingress \
  --group-id $EC2_SG_ID \
  --protocol tcp \
  --port 8000 \
  --cidr 0.0.0.0/0

1.3 Create SSH Key Pair (if needed)

# Create key pair
aws ec2 create-key-pair \
  --key-name oxy-simple-keypair \
  --query 'KeyMaterial' \
  --output text > ~/.ssh/oxy-simple-keypair.pem

# Set proper permissions
chmod 400 ~/.ssh/oxy-simple-keypair.pem

Step 2: Create Configuration Files

2.1 Create Oxy Configuration

Create a local configuration file that will be copied to the server:

cat > config.yml << 'EOF'
databases:
  - name: "local_duckdb"
    type: "duckdb"
    dataset: "/mnt/data/oxy_data/local.db"

models:
  - name: "gpt-4"
    vendor: "openai"
    model_ref: "gpt-4"
    key_var: "OPENAI_API_KEY"

authentication:
  basic:
    smtp_user: "noreply@yourdomain.com"
    smtp_password_var: "SMTP_PASSWORD"
    smtp_server: "smtp.gmail.com"
    smtp_port: 587

defaults:
  database: "local_duckdb"
EOF

2.2 Create Environment Variables File

cat > oxy.env << 'EOF'
OXY_STATE_DIR=/mnt/data/oxy_data
AWS_REGION=us-west-2
OPENAI_API_KEY=your_openai_api_key_here
SMTP_PASSWORD=your_smtp_password_here
EOF

2.3 Create Setup Script

cat > oxy-setup.sh << 'EOF'
#!/bin/bash
set -e

# Update system
sudo apt-get update -y
sudo apt-get install -y curl wget unzip

# Mount EBS volume
DEVICE="/dev/nvme1n1"
MOUNT_POINT="/mnt/data"

sudo mkdir -p $MOUNT_POINT

# Check if device exists and format if needed
if [ -b "$DEVICE" ]; then
    # Check if filesystem exists
    if ! sudo blkid "$DEVICE"; then
        echo "Formatting $DEVICE..."
        sudo mkfs.ext4 "$DEVICE"
    fi
    
    # Mount the device
    sudo mount "$DEVICE" "$MOUNT_POINT"
    
    # Add to fstab for persistence
    echo "$DEVICE $MOUNT_POINT ext4 defaults,nofail 0 2" | sudo tee -a /etc/fstab
    
    # Set permissions
    sudo chown ubuntu:ubuntu "$MOUNT_POINT"
fi

# Install Oxy
curl --proto '=https' --tlsv1.2 -LsSf https://internal.oxy.tech | sudo bash

# Create directories
mkdir -p /mnt/data/oxy_data

# Copy configuration files (these will be uploaded separately)
# Environment file should be placed at /mnt/data/oxy.env
# Config file should be placed at /mnt/data/config.yml

# Create systemd service
sudo tee /etc/systemd/system/oxy.service << 'SERVICE_EOF'
[Unit]
Description=Oxy Application with Built-in Auth
After=network.target

[Service]
Type=forking
User=ubuntu
WorkingDirectory=/mnt/data
EnvironmentFile=/mnt/data/oxy.env
ExecStart=/bin/bash -c '/usr/local/bin/oxy serve --auth-mode built-in --config /mnt/data/config.yml --port 3000 & /usr/local/bin/oxy mcp-sse --port 8000 & wait'
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
SERVICE_EOF

# Enable service (but don't start yet - wait for config files)
sudo systemctl daemon-reload
sudo systemctl enable oxy

echo "Setup complete. Upload config files and start service manually."
EOF

# Make script executable
chmod +x oxy-setup.sh

Step 3: Launch EC2 Instance

3.1 Get Latest Ubuntu ARM64 AMI

# Get latest Ubuntu 22.04 ARM64 AMI ID
AMI_ID=$(aws ec2 describe-images \
  --owners 099720109477 \
  --filters "Name=name,Values=ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-arm64-server-*" \
  --query 'Images | sort_by(@, &CreationDate) | [-1].ImageId' \
  --output text)

echo "Using AMI: $AMI_ID"

3.2 Launch EC2 Instance

# Encode user data script
USER_DATA=$(base64 -i oxy-setup.sh)

# Launch instance
INSTANCE_ID=$(aws ec2 run-instances \
  --image-id $AMI_ID \
  --count 1 \
  --instance-type t4g.small \
  --key-name oxy-simple-keypair \
  --security-group-ids $EC2_SG_ID \
  --subnet-id $SUBNET_ID \
  --user-data "$USER_DATA" \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=oxy-simple-instance}]' \
  --query 'Instances[0].InstanceId' \
  --output text)

echo "Launched instance: $INSTANCE_ID"

# Wait for instance to be running
aws ec2 wait instance-running --instance-ids $INSTANCE_ID
echo "Instance is now running"

3.3 Create and Attach EBS Volume

# Get instance availability zone
AZ=$(aws ec2 describe-instances \
  --instance-ids $INSTANCE_ID \
  --query 'Reservations[0].Instances[0].Placement.AvailabilityZone' \
  --output text)

# Create EBS volume
VOLUME_ID=$(aws ec2 create-volume \
  --size 10 \
  --availability-zone $AZ \
  --volume-type gp3 \
  --tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=oxy-simple-data}]' \
  --query 'VolumeId' \
  --output text)

# Wait for volume to be available
aws ec2 wait volume-available --volume-ids $VOLUME_ID

# Attach volume to instance
aws ec2 attach-volume \
  --volume-id $VOLUME_ID \
  --instance-id $INSTANCE_ID \
  --device /dev/xvdf

echo "Attached volume: $VOLUME_ID"

Step 4: Configure and Start Application

4.1 Get Instance IP and Connect

# Get the instance public IP
INSTANCE_IP=$(aws ec2 describe-instances \
  --instance-ids $INSTANCE_ID \
  --query 'Reservations[0].Instances[0].PublicIpAddress' \
  --output text)

echo "Instance IP: $INSTANCE_IP"

# Wait a few minutes for the user data script to complete
echo "Waiting for instance setup to complete..."
sleep 300

4.2 Upload Configuration Files

# Upload configuration files
scp -i ~/.ssh/oxy-simple-keypair.pem config.yml ubuntu@$INSTANCE_IP:/tmp/
scp -i ~/.ssh/oxy-simple-keypair.pem oxy.env ubuntu@$INSTANCE_IP:/tmp/

# Connect and move files to correct location
ssh -i ~/.ssh/oxy-simple-keypair.pem ubuntu@$INSTANCE_IP << 'REMOTE_EOF'
# Move config files
sudo mv /tmp/config.yml /mnt/data/
sudo mv /tmp/oxy.env /mnt/data/
sudo chown ubuntu:ubuntu /mnt/data/config.yml /mnt/data/oxy.env

# Start the service
sudo systemctl start oxy
sudo systemctl status oxy
REMOTE_EOF

Step 5: Access Your Oxy Instance

5.1 Access the Application

echo "Access your Oxy application at: http://$INSTANCE_IP:3000"

5.2 First-Time Setup

  1. Open your browser and navigate to http://$INSTANCE_IP:3000
  2. You’ll be prompted to create an admin account
  3. Enter your email and password to create the first user
  4. The application will send a verification email (if SMTP is configured)

5.3 Authentication Configuration

With built-in authentication, you can configure:

  • Basic Email/Password: Users register with email and password
  • SMTP Integration: For email verification and password reset
  • Google OAuth (optional): Add Google authentication alongside basic auth

Example configuration in config.yml:

authentication:
  basic:
    smtp_user: "noreply@yourdomain.com"
    smtp_password_var: "SMTP_PASSWORD" 
    smtp_server: "smtp.gmail.com"
    smtp_port: 587
  google:  # Optional
    client_id: "your-google-client-id"
    client_secret_var: "GOOGLE_CLIENT_SECRET"

Step 6: Monitoring and Maintenance

6.1 Check Service Status

# SSH into instance
ssh -i ~/.ssh/oxy-simple-keypair.pem ubuntu@$INSTANCE_IP

# Check service status
sudo systemctl status oxy
sudo journalctl -u oxy -f  # Follow logs

# Check disk usage
df -h /mnt/data

6.2 Application Logs

# View application logs
sudo journalctl -u oxy --since "1 hour ago"

# View specific log files if they exist
ls -la /mnt/data/oxy_data/logs/

6.3 Data Backup

# Create manual snapshot
SNAPSHOT_ID=$(aws ec2 create-snapshot \
  --volume-id $VOLUME_ID \
  --description "Oxy data backup $(date +%Y-%m-%d)" \
  --query 'SnapshotId' \
  --output text)

echo "Created snapshot: $SNAPSHOT_ID"

Troubleshooting

Common Issues

1. Service fails to start

# Check service logs
sudo journalctl -u oxy -n 50

# Check configuration file syntax
/usr/local/bin/oxy validate --config /mnt/data/config.yml

# Verify environment variables
cat /mnt/data/oxy.env

2. Database connection issues

# Check if DuckDB file exists and permissions
ls -la /mnt/data/oxy_data/
sudo chown -R ubuntu:ubuntu /mnt/data/oxy_data/

3. Cannot access application

# Check if service is running
sudo systemctl status oxy

# Check security group allows port 3000
aws ec2 describe-security-groups --group-ids $EC2_SG_ID

# Test port locally
ssh -i ~/.ssh/oxy-simple-keypair.pem ubuntu@$INSTANCE_IP
curl http://localhost:3000/health

4. Email authentication not working

# Check SMTP configuration
# Test SMTP connection manually
telnet smtp.gmail.com 587

# Verify environment variables are set
grep SMTP /mnt/data/oxy.env

Security Best Practices

  1. Restrict SSH access: Update security group to only allow SSH from your IP
  2. Use HTTPS: Set up SSL certificate and nginx proxy for production
  3. Regular updates: Keep the system and Oxy updated
  4. Backup strategy: Implement automated EBS snapshots
  5. Monitor logs: Set up log monitoring and alerting

Scaling Up

When you need more features or scale:

  1. Add Load Balancer: Implement ALB for high availability
  2. Database Migration: Move from DuckDB to PostgreSQL or other production database
  3. Container Migration: Consider moving to the ECS deployment
  4. Authentication Upgrade: Add SSO or move to managed authentication

Cleanup

To destroy the infrastructure:

# Stop and delete instance
aws ec2 terminate-instances --instance-ids $INSTANCE_ID

# Wait for termination
aws ec2 wait instance-terminated --instance-ids $INSTANCE_ID

# Delete EBS volume
aws ec2 delete-volume --volume-id $VOLUME_ID

# Delete security group
aws ec2 delete-security-group --group-id $EC2_SG_ID

# Delete VPC resources
aws ec2 delete-route --route-table-id $ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0
aws ec2 disassociate-route-table --association-id $(aws ec2 describe-route-tables --route-table-ids $ROUTE_TABLE_ID --query 'RouteTables[0].Associations[0].RouteTableAssociationId' --output text)
aws ec2 delete-route-table --route-table-id $ROUTE_TABLE_ID
aws ec2 delete-subnet --subnet-id $SUBNET_ID
aws ec2 detach-internet-gateway --internet-gateway-id $IGW_ID --vpc-id $VPC_ID
aws ec2 delete-internet-gateway --internet-gateway-id $IGW_ID
aws ec2 delete-vpc --vpc-id $VPC_ID

# Delete key pair
aws ec2 delete-key-pair --key-name oxy-simple-keypair
rm ~/.ssh/oxy-simple-keypair.pem

# Clean up local files
rm config.yml oxy.env oxy-setup.sh

Next Steps