Automated deployment of Chainlit applications using Ansible.
This playbook installs Python, creates a virtual environment, and deploys a Chainlit application.
Create inventory.ini:
[chainlit_servers]
chainlit-prod-01 ansible_host=192.168.1.100
chainlit-prod-02 ansible_host=192.168.1.101
[chainlit_servers:vars]
ansible_user=ubuntu
ansible_python_interpreter=/usr/bin/python3
Create chainlit.yml:
---
- name: Deploy Chainlit Application
hosts: chainlit_servers
become: true
vars:
app_name: chainlit
app_dir: /opt/chainlit
python_version: "3.11"
openai_api_key: "{{ vault_openai_api_key }}"
chainlit_auth_secret: "{{ vault_chainlit_auth_secret }}"
tasks:
- name: Install system dependencies
apt:
name:
- python3
- python3-pip
- python3-venv
- python3-dev
- git
- build-essential
- nginx
state: present
update_cache: yes
- name: Create application directory
file:
path: "{{ app_dir }}"
state: directory
mode: '0755'
- name: Create subdirectories
file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- "{{ app_dir }}/public"
- "{{ app_dir }}/data"
- "{{ app_dir }}/logs"
- name: Create Python virtual environment
command: python3 -m venv {{ app_dir }}/venv
args:
creates: {{ app_dir }}/venv/bin/activate
- name: Upgrade pip
pip:
name: pip
state: latest
virtualenv: "{{ app_dir }}/venv"
- name: Install Chainlit
pip:
name:
- chainlit
- python-dotenv
- langchain-openai
virtualenv: "{{ app_dir }}/venv"
- name: Create .env file
copy:
dest: "{{ app_dir }}/.env"
content: |
OPENAI_API_KEY={{ openai_api_key }}
CHAINLIT_AUTH_SECRET={{ chainlit_auth_secret }}
mode: '0600'
- name: Create application script
copy:
dest: "{{ app_dir }}/app.py"
content: |
import chainlit as cl
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
load_dotenv()
@cl.on_chat_start
async def on_chat_start():
llm = ChatOpenAI(model="gpt-4o", temperature=0.7)
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant."),
("human", "{input}")
])
chain = prompt | llm | StrOutputParser()
cl.user_session.set("chain", chain)
await cl.Message(
content="Hello! How can I help you?"
).send()
@cl.on_message
async def on_message(message: cl.Message):
chain = cl.user_session.get("chain")
msg = cl.Message(content="")
await msg.send()
async for chunk in chain.astream({"input": message.content}):
await msg.stream_token(chunk)
await msg.update()
mode: '0644'
- name: Create systemd service
copy:
dest: /etc/systemd/system/chainlit.service
content: |
[Unit]
Description=Chainlit Application
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory={{ app_dir }}
Environment="PATH={{ app_dir }}/venv/bin"
EnvironmentFile={{ app_dir }}/.env
ExecStart={{ app_dir }}/venv/bin/chainlit run app.py --host 0.0.0.0 --port 8000
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
notify:
- Restart Chainlit
- name: Enable and start service
systemd:
name: chainlit
enabled: yes
state: started
daemon_reload: yes
- name: Configure nginx
copy:
dest: /etc/nginx/sites-available/chainlit
content: |
server {
listen 80;
server_name _;
location / {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
notify:
- Restart nginx
- name: Enable nginx site
file:
src: /etc/nginx/sites-available/chainlit
dest: /etc/nginx/sites-enabled/chainlit
state: link
notify:
- Restart nginx
handlers:
- name: Restart Chainlit
systemd:
name: chainlit
state: restarted
daemon_reload: yes
- name: Restart nginx
systemd:
name: nginx
state: restarted
# Basic run
ansible-playbook -i inventory.ini chainlit.yml
# With vault secrets
ansible-playbook -i inventory.ini chainlit.yml --ask-vault-pass
# Limit to specific host
ansible-playbook -i inventory.ini chainlit.yml --limit chainlit-prod-01
# Dry run (check mode)
ansible-playbook -i inventory.ini chainlit.yml --check
Create secrets file:
ansible-vault create group_vars/all/vault.yml
Add your secrets:
vault_openai_api_key: "sk-your-openai-api-key"
vault_chainlit_auth_secret: "your-secret-key"
# Check service status
ansible -i inventory.ini chainlit_servers -m systemd -a "name=chainlit state=started"
# View logs
ansible -i inventory.ini chainlit_servers -m shell -a "journalctl -u chainlit -n 20"
# Test endpoint
ansible -i inventory.ini chainlit_servers -m uri -a "url=http://localhost:8000/health method=GET return_content=yes"
- name: Rolling update Chainlit
hosts: chainlit_servers
become: true
serial: 1 # Update one server at a time
tasks:
- name: Stop service
systemd:
name: chainlit
state: stopped
- name: Update pip packages
pip:
name:
- chainlit
- langchain-openai
state: latest
virtualenv: "{{ app_dir }}/venv"
- name: Start service
systemd:
name: chainlit
state: started
- name: Verify service
systemd:
name: chainlit
state: started
register: result
retries: 3
delay: 10
until: result.status.ActiveState == "active"
- name: Backup Chainlit configuration
hosts: chainlit_servers
become: true
tasks:
- name: Create backup directory
file:
path: "{{ app_dir }}/backups"
state: directory
- name: Backup config files
archive:
path:
- "{{ app_dir }}/.env"
- "{{ app_dir }}/app.py"
- "{{ app_dir }}/public"
dest: "{{ app_dir }}/backups/config-{{ ansible_date_time.date }}.tar.gz"
ansible -i inventory.ini chainlit_servers \
-m systemd -a "name=chainlit"
ansible -i inventory.ini chainlit_servers \
-m shell -a "journalctl -u chainlit --since '1 hour ago'"
ansible -i inventory.ini chainlit_servers \
-m systemd -a "name=chainlit state=restarted"
ansible -i inventory.ini chainlit_servers \
-m shell -a "{{ app_dir }}/venv/bin/pip list | grep chainlit"
ansible -i inventory.ini chainlit_servers \
-m uri -a "url=http://localhost:8000/health method=GET return_content=yes"
See the main Chainlit Setup guide for more details.