Deploying Bottle on Hop3¶
This guide walks you through deploying a Bottle application on Hop3. Bottle is a fast, simple micro-framework for small web applications.
Prerequisites¶
Before you begin, ensure you have:
- A Hop3 server - Follow the Installation Guide
- The Hop3 CLI - Installed on your local machine
- Python 3.10+ - Install from python.org
- Git - For version control and deployment
Verify your local setup:
Step 1: Create a New Bottle Application¶
Install Bottle:
Step 2: Create the Application¶
import os
import json
from datetime import datetime
from bottle import Bottle, request, response, run
app = Bottle()
# In-memory storage
items = {
1: {"id": 1, "name": "Item 1", "price": 9.99},
2: {"id": 2, "name": "Item 2", "price": 19.99},
}
next_id = 3
@app.route('/')
def home():
return f"""
<!DOCTYPE html>
<html>
<head>
<title>Welcome to Hop3</title>
<style>
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: linear-gradient(135deg, #2d3436 0%, #636e72 100%);
color: white;
}}
.container {{ text-align: center; padding: 2rem; }}
h1 {{ font-size: 3rem; margin-bottom: 1rem; }}
p {{ font-size: 1.25rem; opacity: 0.9; }}
</style>
</head>
<body>
<div class="container">
<h1>Hello from Hop3!</h1>
<p>Your Bottle application is running.</p>
<p>Current time: {datetime.now().isoformat()}</p>
</div>
</body>
</html>
"""
@app.route('/up')
def up():
return 'OK'
@app.route('/health')
def health():
response.content_type = 'application/json'
return json.dumps({
"status": "ok",
"timestamp": datetime.now().isoformat(),
"version": "1.0.0"
})
@app.route('/api/info')
def info():
import sys
response.content_type = 'application/json'
return json.dumps({
"name": "hop3-tuto-bottle",
"version": "1.0.0",
"python_version": sys.version,
"framework": "Bottle"
})
@app.route('/api/items')
def list_items():
response.content_type = 'application/json'
return json.dumps(list(items.values()))
@app.route('/api/items/<item_id:int>')
def get_item(item_id):
response.content_type = 'application/json'
if item_id not in items:
response.status = 404
return json.dumps({"error": "Not found"})
return json.dumps(items[item_id])
@app.route('/api/items', method='POST')
def create_item():
global next_id
response.content_type = 'application/json'
data = request.json
item = {"id": next_id, "name": data["name"], "price": data["price"]}
items[next_id] = item
next_id += 1
response.status = 201
return json.dumps(item)
@app.route('/api/items/<item_id:int>', method='DELETE')
def delete_item(item_id):
if item_id not in items:
response.status = 404
return json.dumps({"error": "Not found"})
del items[item_id]
response.status = 204
return ''
if __name__ == '__main__':
port = int(os.environ.get('PORT', 8080))
run(app, host='0.0.0.0', port=port)
Step 3: Create Requirements¶
Step 4: Test the Application¶
Start the server with a timeout and test it:
. venv/bin/activate
# Use a random port to avoid conflicts with other tests
export PORT=$((10000 + RANDOM % 50000))
python app.py &
PID=$!
sleep 3
curl -s http://localhost:$PORT/health || echo "Server not responding"
kill $PID 2>/dev/null || true
Step 5: Create Deployment Configuration¶
[metadata]
id = "hop3-tuto-bottle"
version = "1.0.0"
title = "Hop3 Tutorial - Bottle"
[build]
packages = ["python3", "python3-pip"]
[run]
start = "gunicorn app:app --bind 0.0.0.0:$PORT"
[env]
PYTHONUNBUFFERED = "1"
[port]
web = 8080
[healthcheck]
path = "/up"
timeout = 30
interval = 60
Step 6: Deploy to Hop3¶
Initialize (First Time Only)¶
Deploy¶
Deploy the application (first deployment creates the app):
Set Hostname¶
Configure the hostname for nginx proxy:
Apply Configuration¶
Redeploy to apply the hostname configuration:
Verify Deployment¶
Managing Your Application¶
# Restart the application
hop3 app:restart hop3-tuto-bottle
# View logs
hop3 app:logs hop3-tuto-bottle
# View/set environment variables
hop3 config:show hop3-tuto-bottle
hop3 config:set hop3-tuto-bottle NEW_VAR=value
# Scale workers
hop3 ps:scale hop3-tuto-bottle web=2