This project deploys an API for AWS Bedrock using EC2 and API Gateway. It uses a Node.js server running on a t4g.small instance to handle API requests, with integrated prompt templates and guardrails.
- EC2 (t4g.small) running Node.js server
- API Gateway for request routing
- CloudWatch for logging
- IAM roles for Bedrock access
- Elastic IP for stable addressing
- Systemd service for process management
- AWS CLI configured with appropriate credentials
- Terraform installed
- AWS account with access to Bedrock service
- Access to the specified Bedrock prompt template and guardrail
.
├── README.md
├── variables.tf # Variable definitions
└── main.tf # Main infrastructure configuration
Contains configurable variables:
- AWS region (default: us-west-2)
- Instance type (default: t4g.small)
- Volume size (default: 20GB)
Contains:
- Infrastructure configuration
- Node.js server code
- Systemd service configuration
- API Gateway setup
- Security group rules
- IAM roles and policies
- Clone this repository:
git clone <repository-url>
cd <repository-directory>- Initialize Terraform:
terraform init- Deploy the infrastructure:
terraform apply- After deployment, Terraform will output:
api_url: The API Gateway URL for making requestsserver_ip: The EC2 instance's public IP
Send requests to the API:
curl -X POST http://localhost/invoke \
-H "Content-Type: application/json" \
-d '{
"prompt": "us-west-2",
"model_id": "meta.llama3-2-90b-instruct-v1:0",
"max_tokens": 4000,
"temperature": 0.7
}'- 80: HTTP API
- 22: SSH access
- 11434: Ollama compatibility
- Prompt Template ARN: arn:aws:bedrock:us-west-2:381492005022:prompt/4NLYS6J1L0
- Guardrail ARN: arn:aws:bedrock:us-west-2:381492005022:guardrail/k6tcx8eogg3w
You can run the Bedrock API locally on the EC2 instance using Docker instead of the systemd service.
- Docker installed on the EC2 instance
# Install Docker on Ubuntu
sudo apt-get update
sudo apt-get install docker.io
sudo systemctl enable --now docker
sudo usermod -aG docker ubuntu
# Log out and back in for group changes to take effect- Create a directory for the API:
mkdir bedrock-api
cd bedrock-api- Create two files:
Dockerfile:
FROM node:20-slim
WORKDIR /usr/src/app
# Copy package files
COPY package*.json ./
# Create package.json if it doesn't exist
RUN if [ ! -f package.json ]; then echo '{"name": "bedrock-api","version": "1.0.0","main": "server.js"}' > package.json; fi
# Install dependencies
RUN npm install express @aws-sdk/client-bedrock-runtime
# Copy server code
COPY server.js .
# Set required environment variables
ENV PORT=80
ENV AWS_REGION=us-west-2
# Expose the port
EXPOSE 80
# Start the server
CMD ["node", "server.js"]server.js:
const express = require('express');
const { BedrockRuntimeClient, InvokeModelCommand } = require('@aws-sdk/client-bedrock-runtime');
const app = express();
const port = process.env.PORT || 80;
// Initialize Bedrock client
const bedrock = new BedrockRuntimeClient({
region: process.env.AWS_REGION || 'us-west-2'
});
app.use(express.json());
app.post('/invoke', async (req, res) => {
try {
const { prompt, model_id = 'meta.llama3-2-90b-instruct-v1:0', max_tokens = 1000, temperature = 0.7 } = req.body;
if (!prompt) {
return res.status(400).json({ error: 'prompt is required' });
}
// Prepare request for Bedrock with Llama-specific format and guardrail
const params = {
modelId: model_id,
contentType: "application/json",
accept: "application/json",
body: JSON.stringify({
promptArn: "arn:aws:bedrock:us-west-2:381492005022:prompt/4NLYS6J1L0",
guardrailArn: "arn:aws:bedrock:us-west-2:381492005022:guardrail/k6tcx8eogg3w",
prompt: prompt,
max_gen_len: max_tokens,
temperature: temperature,
top_p: 0.9
})
};
const command = new InvokeModelCommand(params);
const response = await bedrock.send(command);
const responseBody = JSON.parse(new TextDecoder().decode(response.body));
res.json({
completion: responseBody.generation,
model: model_id
});
} catch (error) {
console.error('Error:', error);
res.status(500).json({ error: error.message });
}
});
// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'healthy' });
});
app.listen(port, '0.0.0.0', () => {
console.log(`Server running on port ${port}`);
});- Build the Docker image:
docker build -t bedrock-api .- Run the container:
docker run -d -p 80:80 \
-e AWS_ACCESS_KEY_ID=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/bedrock_access_role | jq -r '.AccessKeyId') \
-e AWS_SECRET_ACCESS_KEY=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/bedrock_access_role | jq -r '.SecretAccessKey') \
-e AWS_SESSION_TOKEN=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/bedrock_access_role | jq -r '.Token') \
-e AWS_REGION=us-west-2 \
--name bedrock-api \
--restart unless-stopped \
bedrock-apiCheck container status:
docker psView logs:
docker logs bedrock-apiStop the container:
docker stop bedrock-apiStart the container:
docker start bedrock-apiRemove the container:
docker rm bedrock-apiTest the health endpoint:
curl http://localhost/healthTest the API:
curl -X POST http://localhost/invoke \
-H "Content-Type: application/json" \
-d '{
"prompt": "us-west-2",
"model_id": "meta.llama3-2-90b-instruct-v1:0",
"max_tokens": 1000,
"temperature": 0.7
}'# Check service status
sudo systemctl status bedrock-api
# Restart service
sudo systemctl restart bedrock-api
# View logs
journalctl -u bedrock-api -f- SSH into the instance
- Navigate to /home/ubuntu
- Update the code
- Restart the service
This setup incurs costs for:
- API Gateway requests
- Bedrock model usage
- CloudWatch logs
- Elastic IP (when not attached to a running instance)
To remove all resources:
terraform destroy-
API not responding:
- Check EC2 instance status
- Verify service is running:
systemctl status bedrock-api - Check security group rules
-
Bedrock errors:
- Verify IAM roles and permissions
- Check prompt template and guardrail access
- Review CloudWatch logs
-
Deployment issues:
- Ensure AWS credentials are configured
- Verify region compatibility
- Check terraform.tfstate file
- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
MIT License