Morphic
Overview
Morphic is an open-source, AI-powered answer engine. You ask a question in plain language, and Morphic searches the web, reads what it finds and streams back an answer with its sources. It renders results into a generative interface rather than a list of blue links, keeps your conversations so you can return to a line of enquiry later, and suggests follow-up questions.
Morphic calls an AI provider. It does not host a model. That keeps it cheap to run: no GPU and one small VM. You bring your own provider key, so you stay in control of which model you use and what it costs.
This SecureAnyCloud image runs Morphic v1.5.0 from the official container image, pinned to an immutable digest. PostgreSQL 16 stores chat history, Redis caches searches and a private SearXNG instance handles web search. All of them are bound to the VM's loopback interface, with nginx in front on port 80.
What you get:
- Morphic v1.5.0 (container
ghcr.io/miurla/morphic, Apache-2.0), pinned to an immutablesha256digest so the shipped version never drifts - A private SearXNG search engine on the VM, so web search works out of the box with no search API key at all
- PostgreSQL 16 for chat history and Redis for the search result cache, both bound to
127.0.0.1with passwords generated per VM - nginx on
:80enforcing HTTP Basic authentication against a per-VM credential, with response buffering disabled so answers stream token by token - An unauthenticated
/healthzendpoint for load balancer and uptime checks - No AI provider key of any kind baked into the image. You add exactly one after launch.
docker.service,postgresql.service,redis-server.service,nginx.service,morphic.serviceandmorphic-firstboot.serviceas enabled, active systemd units- 24/7 SecureAnyCloud support
How this image is secured
Morphic upstream is designed for personal, single-user, local use. Authentication is optional, and the only implementation it ships is hosted Supabase, which the prebuilt container cannot use. Deployed as upstream ships it, a Morphic VM on a public IP with a working provider key would let anyone who finds it spend your AI credits.
Morphic upstream has no self-hosted login of its own, so SecureAnyCloud locks the whole instance behind a sign-in credential that every VM generates for itself on first boot. This image closes the gap in three ways:
- nginx requires a credential on every path except the static
/healthzprobe. The username and a 24-character password are generated on this VM at first boot and written to a root-only file. There is no default, shared or documented credential at any point. - Port 80 stays shut until the credential exists.
nginx.servicecarries a condition on a marker file. First boot creates that file only after it has minted the credential and confirmed that an unauthenticated request is refused. A customer VM cannot serve a single request before it is locked down. - The application does not start until it is configured.
morphic.servicecarries a condition on the per-VM environment file, which is absent from the image. The enabled unit is a no-op until first boot writes the file.
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.
Standard_B2ms (2 vCPU / 8 GiB RAM) is the recommended size. The idle stack uses about 1.1 GiB. However, Morphic's advanced search reads and parses the pages it finds, and that crawl plus Next.js server-side rendering can spike sharply while several answers stream at once. 8 GiB gives real headroom without a swap file.
Standard_B2s (4 GiB) is workable for a single light user. Use Standard_B4ms for a busy shared instance.
Configure the network security group inbound rules to allow 22/tcp from your management network and 80/tcp from wherever you will use Morphic. Morphic serves plain HTTP on port 80. For anything reachable from the internet, terminate TLS in front of it and use a real hostname. Use an Azure Application Gateway or Front Door, or your own certificate on the VM's nginx.
Nothing else needs opening. The application itself (3000), PostgreSQL (5432), Redis (6379) and SearXNG (8080) are all bound to the loopback interface and are unreachable from outside the VM.
You also need an API key from one AI provider: OpenAI, Anthropic, Google Gemini, the Vercel AI Gateway, or any OpenAI-compatible endpoint. You do not need a search provider key, because this image runs its own search engine.
Step 1 - Deploy from the Azure Marketplace
In the Azure portal, choose Create a resource, search for Morphic on Ubuntu 24.04 LTS by SecureAnyCloud, and select Create.
Pick your subscription, resource group and region, and set the size to Standard_B2ms. Choose SSH public key authentication with username azureuser, and allow inbound ports 22 and 80.
Step 2 - Deploy from the Azure CLI
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:morphic-ubuntu-24-04:default:latest
az vm create \
--resource-group my-morphic-rg \
--name my-morphic \
--image sac1647283583153:morphic-ubuntu-24-04:default:latest \
--size Standard_B2ms \
--storage-sku StandardSSD_LRS \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard \
--os-disk-delete-option Delete --nic-delete-option Delete
# then open the port Morphic serves on
az vm open-port --resource-group my-morphic-rg --name my-morphic --port 80 --priority 900Run both commands on your own workstation, not on the VM.
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>First boot takes roughly a minute. It generates this VM's secrets, starts the containers and only then opens port 80. If the site does not answer immediately, wait a moment and retry.
Step 4 - Confirm the services are running
sudo systemctl is-active docker postgresql redis-server nginx morphic morphic-firstbootThe expected output is six active lines:
active active active active active activeThen confirm that both containers are up:
sudo docker ps --format 'table {{.Names}}\t{{.Status}}'NAMES STATUS morphic Up About a minute morphic-searxng Up About a minuteStep 5 - Confirm only port 80 is exposed
List the listening TCP sockets:
sudo ss -tlnH | awk '{print $4}' | grep -E ':(80|3000|5432|6379|8080)$' | sort -uEverything except nginx is on loopback:
0.0.0.0:80 127.0.0.1:3000 127.0.0.1:5432 127.0.0.1:6379 127.0.0.1:8080 [::1]:6379 [::]:80Only :80 accepts connections from the network, and nginx guards that port with your credential. The application, database, cache and search engine are reachable only from the VM itself.
Step 6 - Retrieve your per-VM sign-in credential
Every VM generates its own username and password on first boot and writes them to a root-only file. Read the file with sudo:
sudo cat /root/morphic-credentials.txt# Morphic - generated on first boot by morphic-firstboot.service
# These values are unique to this VM. Store them somewhere safe. morphic.url=http://10.0.0.11/ morphic.web.username=sac-401b30 morphic.web.password=************************ morphic.redis.password=******************************** morphic.postgres.password=********************************The username is randomised per VM, so no two SecureAnyCloud Morphic instances share one. The morphic.url entry records the address the VM resolved for itself at first boot. If your VM has a public IP, browse to that address instead.
Step 7 - Confirm the instance refuses anonymous access
Do this check before you put a provider key on the machine. An unauthenticated request must be refused:
curl -s -o /dev/null -w 'anonymous: HTTP %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'default admin:admin: HTTP %{http_code}\n' -u admin:admin http://127.0.0.1/
curl -s -o /dev/null -w 'healthz: HTTP %{http_code}\n' http://127.0.0.1/healthzanonymous: HTTP 401 default admin:admin: HTTP 401 healthz: HTTP 200The chat API is the route that would spend your provider credits. It is refused the same way:
curl -s -o /dev/null -w 'anonymous chat API: HTTP %{http_code}\n' \
-X POST -H 'Content-Type: application/json' -d '{"messages":[]}' \
http://127.0.0.1/api/chatanonymous chat API: HTTP 401Then confirm that your own credential is accepted:
curl -s -o /dev/null -w 'per-VM credential: HTTP %{http_code}\n' \
-u '<MORPHIC_WEB_USERNAME>:<MORPHIC_WEB_PASSWORD>' http://127.0.0.1/per-VM credential: HTTP 200Step 8 - Add your AI provider key
This is the one thing you must configure. Morphic calls an external model, and this image ships no key of any kind. Until you add one, Morphic loads but cannot answer.
Open the environment file:
sudo nano /etc/morphic/morphic.envAt the bottom of the file, there is one commented line per supported provider:
# OPENAI_API_KEY=
# ANTHROPIC_API_KEY=
# GOOGLE_GENERATIVE_AI_API_KEY=
# AI_GATEWAY_API_KEY=
# OLLAMA_BASE_URL=http://127.0.0.1:11434
# OPENAI_COMPATIBLE_API_KEY=
# OPENAI_COMPATIBLE_API_BASE_URL=Uncomment exactly one line, paste your key after the =, save the file, and restart the service:
sudo systemctl restart morphicMorphic detects which providers have keys and lists their models in the picker at the bottom right of the compose box. Notes on providers:
- OpenAI, Anthropic, Google and the Vercel AI Gateway are the straightforward choices. Set the key and the models appear.
- Any OpenAI-compatible endpoint (DeepSeek, Moonshot, Together, your own gateway) works when you set
OPENAI_COMPATIBLE_API_KEYandOPENAI_COMPATIBLE_API_BASE_URLtogether. If the provider has no/v1/modelsendpoint, also setOPENAI_COMPATIBLE_MODELSto a comma-separated list. - Ollama, for a fully local model, must be installed separately. The model must support both tool calling and reasoning. Morphic requests a thinking stream, and a model without it returns
does not support thinking. Qwen3 models work and Llama 3.2 does not. A CPU-only VM this size is slow, so a hosted provider is the better choice for everyday use.
Your key is stored in a 0600 root-owned file. It is never written to the image, a log or the database.
Step 9 - Web search, with no search API key
Search is the other half of Morphic. This image runs its own private SearXNG instance on 127.0.0.1:8080, so search works before you add any provider key. Run a test query:
curl -s -m 90 -u '<MORPHIC_WEB_USERNAME>:<MORPHIC_WEB_PASSWORD>' \
-X POST -H 'Content-Type: application/json' \
-d '{"query":"azure virtual machine","maxResults":3,"searchDepth":"basic"}' \
http://127.0.0.1/api/advanced-search | jq -r '.results[] | .title'The query returns real, live results:
Microsoft Azure Microsoft Azure portal Azure Virtual MachinesTo use a commercial search provider instead, set SEARCH_API=tavily (or exa, or firecrawl) in /etc/morphic/morphic.env along with that provider's key, and restart. Most deployments do not need this.
Step 10 - Sign in to the web interface
Browse to http://<vm-public-ip>/. When the browser asks for a username and password, enter morphic.web.username and morphic.web.password from Step 6.
After the prompt, Morphic's home screen opens. It has an ask box, the model picker showing whichever provider you configured, a web search toggle, and shortcut chips for common intents.
Step 11 - Ask your first question
Type a question and press Enter. Morphic searches, reads the results and streams an answer back as it is generated, showing the model's working as it goes.
When the answer completes, Morphic offers follow-up questions and stores the conversation. Open the sidebar from the top left to see your history. It is held in the PostgreSQL database on this VM and nowhere else.
Answer quality depends on the model you chose. A frontier model gives noticeably better synthesis and citation than a small local one.
Step 12 - Confirm the datastores are private
Redis refuses unauthenticated commands. Try it without a password:
redis-cli -h 127.0.0.1 PINGNOAUTH Authentication required.Redis answers when you use this VM's own password:
redis-cli -h 127.0.0.1 -a '<MORPHIC_REDIS_PASSWORD>' --no-auth-warning PINGPONGPostgreSQL holds the chat history and accepts only this VM's password. List its tables:
PGPASSWORD='<MORPHIC_POSTGRES_PASSWORD>' psql -w -h 127.0.0.1 -U morphic -d morphic \
-tAc "SELECT string_agg(table_name, ', ' ORDER BY table_name) FROM information_schema.tables WHERE table_schema='public';"chats, feedback, messages, partsNeither port is reachable from outside the VM. The postgres superuser has no password at all and uses local socket authentication only.
Step 13 - Confirm the shipped images are the pinned versions
Inspect the container images:
sudo docker inspect --format '{{.Config.Image}}' morphic
sudo docker inspect --format '{{.Config.Image}}' morphic-searxngghcr.io/miurla/morphic@sha256:15c7c4be2e4ec4fe3c6fd7f47c51eb74745428a58b6c65bca4a4ce7aef3e59f3 searxng/searxng@sha256:854f239d5c181db9c4282cd0ffc1737a61232dd6ad0ea964028b96df17d7758aBoth images are referenced by immutable digest rather than a moving tag, so the software on this VM cannot change underneath you. The Morphic digest is the image that upstream's own release pipeline built from the commit tagged v1.5.0.
Changing the sign-in credential
To set your own password for the existing user:
sudo htpasswd -B /etc/nginx/morphic.htpasswd '<MORPHIC_WEB_USERNAME>'
sudo systemctl reload nginxTo use a different username, add it and remove the old one:
sudo htpasswd -B /etc/nginx/morphic.htpasswd alice
sudo htpasswd -D /etc/nginx/morphic.htpasswd '<MORPHIC_WEB_USERNAME>'
sudo systemctl reload nginxUpdate your own record of the credential afterwards. /root/morphic-credentials.txt is written once at first boot and is not rewritten.
Sharing the instance
This is a single-tenant appliance. Everyone who signs in shares one Morphic identity and therefore one chat history. That is how upstream behaves when its optional Supabase authentication is not configured, and the prebuilt container cannot enable it.
Adding more Basic auth users controls who may reach the instance, not who sees which conversations. If you need per-user histories, run one VM per person, or build Morphic from source against your own Supabase project.
Serving Morphic on your own domain
Terminate TLS in front of the VM with an Azure Application Gateway or Front Door pointed at port 80. Alternatively, add your own certificate to nginx at /etc/nginx/sites-available/sac-morphic and open 443.
Keep the auth_basic directives in place when you edit that file. They stop strangers from using your provider account. Morphic streams its answers, so if you put your own proxy in front, disable response buffering there too.
Maintenance
unattended-upgrades applies Ubuntu security updates automatically. To restart the stack after changing configuration, run:
sudo systemctl restart morphicTo view the application logs, run:
sudo docker logs --tail 50 morphicTo back up your chat history, run:
sudo -u postgres pg_dump morphic > ~/morphic-backup.sqlSupport
SecureAnyCloud provides 24/7 support for this image. Contact us at sales@secureanycloud.com.
Deploy on Azure
Launch Morphic on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com