Skip to content

Instantly share code, notes, and snippets.

@faelmori
Last active June 20, 2025 17:04
Show Gist options
  • Save faelmori/463092315840a09417268b13f8fee1a8 to your computer and use it in GitHub Desktop.
Save faelmori/463092315840a09417268b13f8fee1a8 to your computer and use it in GitHub Desktop.
Run Open-WebUI and Ollama using `docker-compose`

🚀 Mori Ollama Stack (Open-WebUI + Ollama via Docker Compose)

This repository sets up a ready-to-use environment for running Ollama models with Open-WebUI, allowing local experimentation with LLMs using Docker Compose.


📦 Installation

git clone https://github.com/rafa-mori/mori-ollama-srv.git
cd mori-ollama-srv
make dev

To install using the advanced script with custom flags:

bash support/setup-dev.sh --light        # Use lightweight model
bash support/setup-dev.sh --no-benchmark # Skip benchmark step
bash support/setup-dev.sh --remote=user@host

Or simply use Make:

make install ARGS="--light"
make install ARGS="--remote=user@host"

🧠 Supported Models

Model Name Description
deepseek-coder:6.7b Coding-focused LLM with structured responses. Great for code generation.
mistral Lightweight general-purpose LLM. Lower RAM usage and faster loading.
llama Meta's LLM family — generalist models with decent coding ability.

To change models:

make install ARGS="--light"          # Use mistral
make install ARGS="--no-benchmark"   # Skip testing

🛠️ Makefile Targets

Command Description
make dev Optimizes CPU + starts everything with recommended model.
make up Starts the Docker stack.
make pull Pulls the default model (deepseek-coder:6.7b).
make run Runs the model interactively (via CLI inside container).
make logs Shows the WebUI logs.
make clean Clean up all Docker artifacts and volumes.
make test Re-runs setup-dev.sh with benchmarking.
make help Displays all available commands and options.

You can also pass flags like this:

make install ARGS="--remote=user@host"
make install ARGS="--light --no-benchmark"

📂 Project Structure

mori-ollama-srv/
├── docker-compose.yaml
├── support/
│   └── setup-dev.sh         # Main setup script
├── Makefile                 # Developer commands and automation
├── secrets/                 # Optional environment overrides
└── README.md

🔍 Benchmarking

The script auto-benchmarks response time on the first call to ensure your setup is responsive. You can skip benchmarking with:

make install ARGS="--no-benchmark"

🌍 Access the UI

After starting:

http://localhost:3000


📄 License

MIT License — Rafael Mori (c) 2025


💬 Contributing

PRs, issues, and suggestions are always welcome!


📧 Contact

# llm-app-vol/config.toml
[ollama]
num_thread = 6
num_batch = 8
num_ctx = 2048
f16 = true
low_vram = true
name: 'mori-ollama-srv'
services:
# LLM UI Service
llm-ui:
container_name: mori-ollama-srv-llm-ui
image: ghcr.io/open-webui/open-webui:main
restart: unless-stopped
env_file:
- path: secrets/.llm.ui.dev.env
required: false
- path: secrets/.llm.ui.prd.env
required: false
networks:
- hub-ass-priv-net
- hub-ass-pub-net
ports:
- "3000:8080"
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- llm-ui-vol:/app/backend/data
depends_on:
- llm-app
# LLM Server Service
llm-app:
container_name: mori-ollama-srv-llm-app
image: ollama/ollama
restart: unless-stopped
privileged: true
mem_limit: 4096m
cpu_count: 4
env_file:
- path: secrets/.llm.app.dev.env
required: false
- path: secrets/.llm.app.prd.env
required: false
networks:
- hub-ass-priv-net
ports:
- "11434:11434"
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- llm-app-vol:/root/.ollama
deploy:
resources:
limits:
memory: 14g
cpus: '6.0'
networks:
hub-ass-priv-net:
hub-ass-pub-net:
volumes:
llm-ui-vol:
llm-app-vol:
# Description: Makefile for building and managing a Docker-based LLM application
# This Makefile provides targets for setting up, running, and managing the application,
# including performance optimizations, pulling models, and cleaning up build artifacts.
# Author: Rafael Mori
# Copyright (c) 2025 Rafael Mori
# License: MIT License
APP_NAME := $(shell echo $(basename $(CURDIR)) | tr '[:upper:]' '[:lower:]')
ROOT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
# Define the color codes
COLOR_GREEN := \033[32m
COLOR_YELLOW := \033[33m
COLOR_RED := \033[31m
COLOR_BLUE := \033[34m
COLOR_RESET := \033[0m
# Logging Functions
log = @printf "%b%s%b %s\n" "$(COLOR_BLUE)" "[LOG]" "$(COLOR_RESET)" "$(1)"
log_info = @printf "%b%s%b %s\n" "$(COLOR_BLUE)" "[INFO]" "$(COLOR_RESET)" "$(1)"
log_success = @printf "%b%s%b %s\n" "$(COLOR_GREEN)" "[SUCCESS]" "$(COLOR_RESET)" "$(1)"
log_warning = @printf "%b%s%b %s\n" "$(COLOR_YELLOW)" "[WARNING]" "$(COLOR_RESET)" "$(1)"
log_break = @printf "%b%s%b\n" "$(COLOR_BLUE)" "[INFO]" "$(COLOR_RESET)"
log_error = @printf "%b%s%b %s\n" "$(COLOR_RED)" "[ERROR]" "$(COLOR_RESET)" "$(1)"
ARGUMENTS := $(MAKECMDGOALS)
INSTALL_SCRIPT=$(ROOT_DIR)setup-dev.sh
CMD_STR := $(strip $(firstword $(ARGUMENTS)))
ARGS := $(filter-out $(strip $(CMD_STR)), $(ARGUMENTS))
perfmode:
@echo "⚙️ Setting CPU governor to performance..."
@if sudo -v 2>/dev/null; then \
echo "✅ Sudo permissions granted."; \
else \
echo "❌ Sudo permissions required. Please run with sudo."; \
exit 1; \
fi
@for CPUFREQ in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do \
echo performance | sudo tee $$CPUFREQ; \
done
@echo "✅ CPU governor set to performance."
up:
docker compose up -d --build --force-recreate
pull:
docker exec -it llm-app ollama pull deepseek-coder:6.7b
run:
docker exec -it llm-app ollama run deepseek-coder:6.7b
logs:
docker logs -f llm-ui
dev: perfmode up pull
@echo "🚀 Environment is up and optimized! Access: http://localhost:3000"
clean:
$(call log_info, Cleaning up build artifacts)
$(call log_info, Args: $(ARGS))
read -p "Are you sure you want to clean up all build artifacts? (y/n): " confirm && \
if [ "$$confirm" = "y" ]; then \
echo "Cleaning up..."; \
docker compose down --rmi all --volumes --remove-orphans || true; \
rm -rf ./*-vol || true; \
echo "Cleanup complete."; \
else \
echo "Cleanup aborted."; \
fi
$(shell exit 0)
test:
$(call log_info, Running tests and benchmarks)
$(call log_info, Args: $(ARGS))
docker compose config --services | grep -q 'llm-app' && \
@bash $(INSTALL_SCRIPT)
$(shell exit 0)
%:
@:
$(call log_info, Running command: $(CMD_STR))
$(call log_info, Args: $(ARGS))
@bash $(INSTALL_SCRIPT) $(CMD_STR) $(ARGS)
$(shell exit 0)
help:
$(call log, $(APP_NAME) Makefile)
$(call log_break)
$(call log, Usage:)
$(call log, make [target] [ARGS='--custom-arg value'])
$(call log_break)
$(call log, Available targets:)
$(call log, make dev - Starts everything optimized and pulls the recommended model)
$(call log, make perfmode - Sets CPU governor to performance mode on Linux)
$(call log, make up - Starts Docker services)
$(call log, make pull - Pulls the recommended model)
$(call log, make run - Runs the model directly)
$(call log, make logs - Shows UI logs)
$(call log, make clean - Cleans build artifacts)
$(call log, make test - Runs tests)
$(call log, make help - Shows this help message)
$(call log_break)
$(call log, Usage with arguments:)
$(call log, make install ARGS='--custom-arg value' - Pass custom arguments to the install script)
$(call log_break)
$(call log, Examples:)
$(call log, make dev)
$(call log, make install ARGS='--prefix /usr/local')
$(call log, make up)
$(call log, make pull)
$(call log, make run)
$(call log, make logs)
$(call log_break)
$(call log, Available models:)
$(call log, deepseek-coder:6.7b (default) -> make dev or make pull)
$(call log, mistral (lightweight) -> make install ARGS='--light')
$(call log_break)
$(call log, For remote setup:)
$(call log, make install ARGS='--remote=user@remote-host')
$(call log_break)
$(call log, To skip benchmark:)
$(call log, make install ARGS='--no-benchmark')
$(call log_break)
$(call log, Access the UI at: http://localhost:3000)
$(call log_break)
$(call log, For more information, visit:)
$(call log, 'https://github.com/rafa-mori/'$(APP_NAME))
$(call log_break)
$(call log_success, End of help message)
$(shell exit 0)
#!/usr/bin/env bash
# Build: 2025-06-20 15:43:06
set -euo pipefail
# === CONFIG ================================
PROJECT_NAME="mori-ollama-srv"
DEFAULT_MODEL="deepseek-coder:6.7b"
LIGHT_MODEL="mistral"
WEBUI_PORT=3000
OLLAMA_PORT=11434
WEBUI_URL="http://localhost:$WEBUI_PORT"
PROMPT_TEST="Write a 'Hello World' in Go."
# ===========================================
# === FLAGS ================================
USE_LIGHT=false
RUN_MODE=local
REMOTE_HOST=""
DO_BENCHMARK=true
# ===========================================
_SUCCESS="\033[0;32m"
_WARN="\033[0;33m"
_ERROR="\033[0;31m"
_INFO="\033[0;36m"
_NC="\033[0m"
log() {
local type=${1:-info}
local message=${2:-}
case $type in
info|_INFO|-i|-I)
printf '%b[_INFO]%b ℹ️ %s\n' "$_INFO" "$_NC" "$message"
;;
warn|_WARN|-w|-W)
printf '%b[_WARN]%b ⚠️ %s\n' "$_WARN" "$_NC" "$message"
;;
error|_ERROR|-e|-E)
printf '%b[_ERROR]%b ❌ %s\n' "$_ERROR" "$_NC" "$message"
;;
success|_SUCCESS|-s|-S)
printf '%b[_SUCCESS]%b ✅ %s\n' "$_SUCCESS" "$_NC" "$message"
;;
*)
printf '%b[_INFO]%b ℹ️ %s\n' "$_INFO" "$_NC" "$message"
;;
esac
}
clear_screen() {
printf "\033[H\033[2J"
}
get_current_shell() {
local shell_proc
shell_proc=$(cat /proc/$$/comm)
case "${0##*/}" in
${shell_proc}*)
local shebang
shebang=$(head -1 "$0")
printf '%s\n' "${shebang##*/}"
;;
*)
printf '%s\n' "$shell_proc"
;;
esac
}
# Create a temporary directory for cache
_TEMP_DIR="${_TEMP_DIR:-$(mktemp -d)}"
if [[ -d "${_TEMP_DIR}" ]]; then
log info "Temporary directory created: ${_TEMP_DIR}"
else
log error "Failed to create temporary directory."
fi
clear_script_cache() {
trap - EXIT HUP INT QUIT ABRT ALRM TERM
if [[ ! -d "${_TEMP_DIR}" ]]; then
exit 0
fi
rm -rf "${_TEMP_DIR}" || true
if [[ -d "${_TEMP_DIR}" ]] && sudo -v 2>/dev/null; then
sudo rm -rf "${_TEMP_DIR}"
if [[ -d "${_TEMP_DIR}" ]]; then
log error "Failed to remove temporary directory: ${_TEMP_DIR}"
else
log success "Temporary directory removed: ${_TEMP_DIR}"
fi
fi
exit 0
}
create_docker_networks() {
if docker network ls | grep -q "hub-ass-priv-net"; then
log warn "Network 'hub-ass-priv-net' already exists, skipping creation."
else
echo "🌐 Creating Docker networks..."
docker network create hub-ass-priv-net --subnet=10.0.0.0/28 &>/dev/null || true
fi
if docker network ls | grep -q "hub-ass-pub-net"; then
log warn "Network 'hub-ass-pub-net' already exists, skipping creation."
else
docker network create hub-ass-pub-net &>/dev/null || true
fi
}
set_trap() {
local current_shell=""
current_shell=$(get_current_shell)
case "${current_shell}" in
*ksh|*zsh|*bash)
declare -a FULL_SCRIPT_ARGS=("$@")
if [[ "${FULL_SCRIPT_ARGS[*]}" =~ -d ]]; then
set -x
fi
if [[ "${current_shell}" == "bash" ]]; then
set -o errexit
set -o pipefail
set -o errtrace
set -o functrace
shopt -s inherit_errexit
fi
trap 'clear_script_cache' EXIT HUP INT QUIT ABRT ALRM TERM
;;
esac
}
print_banner() {
cat << "EOF"
███╗ ███╗ ██████╗ ██████╗ ██╗
████╗ ████║██╔═══██╗██╔══██╗██║
██╔████╔██║██║ ██║██████╔╝██║
██║╚██╔╝██║██║ ██║██╔══██╗██║
██║ ╚═╝ ██║╚██████╔╝██║ ██║██║
╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ⚙️ Ollama Dev Setup
EOF
}
parse_args() {
for arg in "$@"; do
case $arg in
--light) USE_LIGHT=true ;;
--remote=*) RUN_MODE=remote; REMOTE_HOST="${arg#*=}" ;;
--no-benchmark) DO_BENCHMARK=false ;;
esac
done
}
set_cpu_performance_mode() {
log info "Setting CPU governor to performance..."
for CPUFREQ in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo performance | sudo tee "$CPUFREQ" >/dev/null
done
log success "CPU set to performance mode."
}
check_containers() {
log info "Checking container health..."
unhealthy=$(docker ps --filter health=unhealthy --format "{{.Names}}")
if [[ -n "$unhealthy" ]]; then
log warn "Found unhealthy containers: $unhealthy"
docker restart "$unhealthy"
fi
}
start_services() {
log info "Starting Docker Compose..."
docker compose -p "$PROJECT_NAME" up -d --build --force-recreate
sleep 2
check_containers
}
pull_model() {
local MODEL_NAME=$1
log info "Pulling model: $MODEL_NAME..."
docker exec -i "${PROJECT_NAME}-llm-app-1" ollama pull "$MODEL_NAME"
}
run_benchmark() {
local MODEL_NAME=$1
log info "Benchmarking model response time..."
time curl -s "http://localhost:$OLLAMA_PORT/api/generate" -d '{
"model": "'"$MODEL_NAME"'",
"prompt": "'"$PROMPT_TEST"'"
}' | jq -r '.response'
}
create_docker_volumes() {
log info "Creating Docker volumes..."
if ! test -d "$(realpath ./)/llm-ui-vol"; then
log info "Creating llm-ui-vol directory..."
mkdir -p "$(realpath ./)/llm-ui-vol"
fi
if docker volume ls | grep -q "llm-ui-vol"; then
log warn "Volume 'llm-ui-vol' already exists, skipping creation."
else
docker volume create --driver local --opt type=none --opt device="$(realpath ./)/llm-ui-vol" --opt o=bind "llm-ui-vol"
fi
if ! test -d "$(realpath ./)/llm-app-vol"; then
log info "Creating llm-app-vol directory..."
mkdir -p "$(realpath ./)/llm-app-vol"
fi
if docker volume ls | grep -q "llm-app-vol"; then
log warn "Volume 'llm-app-vol' already exists, skipping creation."
else
docker volume create --driver local --opt type=none --opt device="$(realpath ./)/llm-app-vol" --opt o=bind "llm-app-vol"
fi
return 0
}
open_webui() {
if command -v xdg-open &>/dev/null; then
log info "Opening WebUI at $WEBUI_URL"
xdg-open "$WEBUI_URL" & disown
else
log info "Access WebUI at: $WEBUI_URL"
fi
}
run_local_stack() {
print_banner
parse_args "$@"
set_cpu_performance_mode
create_docker_networks
create_docker_volumes
log info "Using model: $([ "$USE_LIGHT" = true ] && echo "$LIGHT_MODEL" || echo "$DEFAULT_MODEL")"
log info "Project name: $PROJECT_NAME"
log info "WebUI URL: $WEBUI_URL"
log info "Ollama API URL: http://localhost:$OLLAMA_PORT"
MODEL_NAME=$([ "$USE_LIGHT" = true ] && echo "$LIGHT_MODEL" || echo "$DEFAULT_MODEL")
start_services
pull_model "$MODEL_NAME"
if [ "$DO_BENCHMARK" = true ]; then
run_benchmark "$MODEL_NAME"
fi
open_webui
log success "Ready! Model '$MODEL_NAME' is up and running."
}
run_remote_stack() {
# shellcheck disable=SC2029
ssh "$REMOTE_HOST" 'bash -s' < "$0" "${@/--remote=*/}"
}
main() {
case "$(get_current_shell)" in
*ksh|*zsh|*bash)
set_trap "$@"
;;
*)
log error "Unsupported shell: $(get_current_shell)"
exit 1
;;
esac
clear_screen
case "$RUN_MODE" in
help|-h|--help)
echo "Usage: $0 [--light] [--remote=<host>] [--no-benchmark]"
echo "Commands:"
echo " start Start the Mori Ollama stack"
echo " stop Stop the Mori Ollama stack"
echo " restart Restart the Mori Ollama stack"
echo " status Show the status of running containers"
echo " logs Show logs of running containers"
echo " health-check Check the health of running containers"
echo " create-volumes Create necessary Docker volumes"
echo " local Run the stack locally (default)"
echo " remote Run the stack on a remote host"
echo " benchmark Run a benchmark on the default model ($DEFAULT_MODEL)"
echo " help Show this help message"
echo "Options:"
echo " --light Use a lighter model (default: $LIGHT_MODEL)"
echo " --remote=<host> Run setup on a remote host via SSH"
echo " --no-benchmark Skip the benchmark step"
return 0
;;
start|--start)
log info "Starting the Mori Ollama stack..."
run_local_stack "$@"
return $?
;;
stop|--stop)
log info "Stopping the Mori Ollama stack..."
docker compose -p "$PROJECT_NAME" down
log success "Stack stopped."
return $?
;;
restart|--restart)
log info "Restarting the Mori Ollama stack..."
docker compose -p "$PROJECT_NAME" down
sleep 1
run_local_stack "$@"
return $?
;;
status|--status)
log info "Checking status of running containers..."
docker ps --filter "name=$PROJECT_NAME" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
return $?
;;
logs|--logs)
docker compose logs -f "$PROJECT_NAME"
return $?
;;
remote|--remote=*)
run_remote_stack "$@"
return $?
;;
local|--local)
run_local_stack "$@"
return $?
;;
benchmark|--benchmark)
run_benchmark "$DEFAULT_MODEL"
return $?
;;
health-check|--health-check)
check_containers
return $?
;;
create-volumes|--create-volumes)
create_docker_volumes
return $?
;;
*)
log error "Invalid run mode: $RUN_MODE"
return 1
;;
esac
}
main "$@"
exit $?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment