VallottonDocs

Deployment

This chapter explains in detail how to put Vallotton on a server. Once it is deployed, you can open Vallotton from anywhere (including your phone) through your own domain name, and the painting happens on the server too, so your own computer does not have to stay on. The chapter assumes you can log in to a Linux server and work on its command line. If you only want to use Vallotton on your own computer, the quick start is all you need.

What you need

Deployment steps

Once deployed, Vallotton runs inside Docker on the server and opens a single port on the server itself only (8300 by default). When your domain is visited from outside, the request first reaches nginx on the server, which handles HTTPS, and is then passed on to Vallotton. The steps are explained in order below.

1. Download the code

Log in to the server and download the code to a fixed location, for example /srv/vallotton/app. Also create a directory just for the finished pictures, for example /srv/vallotton/media. The rest of this chapter uses these two paths as the example; if you choose different ones, adjust the commands below to match.

sudo mkdir -p /srv/vallotton/media
sudo chown -R $USER /srv/vallotton
git clone https://github.com/FFTYYY/ProjectVallotton.git /srv/vallotton/app
cd /srv/vallotton/app

All the commands below assume you are in this code directory, /srv/vallotton/app.

2. Fill in .env

All of Vallotton's configuration, including every key and password, lives in the .env file in the code directory. Start by copying the template:

cp .env.example .env

Then open it in an editor (for example nano .env) and change these items:

ItemWhat to enter
LLM_PROVIDERWhich language model to use. Only deepseek or grok are supported at the moment. The template defaults to deepseek.
DEEPSEEK_API_KEYYour DeepSeek API key. Needed only when LLM_PROVIDER is deepseek. Starts with sk-.
GROK_API_KEYYour Grok API key. Needed only when LLM_PROVIDER is grok. Starts with xai-.
NOVELAI_API_KEYYour NovelAI key. Starts with pst-.
APP_USERNAME
APP_PASSWORD
The username and password for logging in to the Vallotton web page.
ADMIN_PASSWORDThe password for the admin backend. You will hardly ever need it; just set a long random password.
DJANGO_SECRET_KEYA string of random characters, used to protect the login state.
POSTGRES_PASSWORDThe database password. You will never use it yourself, so again a string of random characters is fine.
DOMAINYour domain name, without the https:// prefix. For example vallotton.example.com.
MEDIA_DIRThe directory for the pictures. Enter the /srv/vallotton/media you created in the previous step.

The following items are optional but commonly used:

The rest can usually stay at their defaults. The complete list is in the environment variables appendix at the end.

3. Start

In the code directory, run:

docker compose up -d --build

The first start downloads and builds a few Docker images, which takes a few minutes. When it is done, check with these two commands:

docker compose ps
curl -s http://127.0.0.1:8300/api/auth/me

The first should list three containers, db, backend and web, all with status Up. The second should return {"authenticated": false}, which means Vallotton is running on port 8300 of the server itself.

It cannot be reached from the internet yet, because port 8300 is open only to the server itself. Access from outside goes through nginx, which is configured in the next step. If port 8300 is already taken by another program on the server, change VALLOTTON_HTTP_PORT in .env to another port and run docker compose up -d again; the nginx configuration in the next step then has to change to match.

4. Domain and HTTPS

The code ships with an nginx configuration template, deploy/host-nginx.conf.example, which forwards visits to your domain on to Vallotton. The commands below configure nginx automatically. In these commands, and in the rest of the chapter, the example domain is vallotton.example.com. Before running them, replace vallotton.example.com with your own domain.

sudo sh -c 'sed "s/YOUR_DOMAIN/vallotton.example.com/g" deploy/host-nginx.conf.example > /etc/nginx/sites-available/vallotton'
sudo ln -s /etc/nginx/sites-available/vallotton /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Then get an HTTPS certificate with certbot (again, replace the example domain vallotton.example.com with your own). It edits the configuration you just created to add HTTPS, and makes http visits redirect to https automatically:

sudo certbot --nginx -d vallotton.example.com

The certificate is valid for 90 days. certbot renews it by itself before it expires, so nothing more is needed.

If your domain is on Cloudflare: before requesting the certificate with certbot, switch that record to “DNS only” (the grey cloud) in Cloudflare, and switch it back to proxied once the certificate has been issued.

You can also skip certbot and use a Cloudflare Origin Certificate instead: generate one in Cloudflare under SSL/TLS → Origin Server. It is valid for 15 years and never needs renewing. Put the certificate and the private key on the server, and add a server block for port 443 to the nginx configuration that points at them. A certificate of this kind only works for visits that go through Cloudflare's proxy.

Whichever certificate you use, Cloudflare's SSL/TLS mode must be “Full (strict)”, not “Flexible”; otherwise the page opens but you cannot log in.

5. Open the page

If all went well, open https://vallotton.example.com (your own domain) in a browser and log in to Vallotton with the APP_USERNAME and APP_PASSWORD you set in .env. From here on, everything works exactly as described in the quick start and the user guide. You can also install Vallotton as an app on your phone; see on a phone in the user guide.

One thing to keep in mind: you must open it through https and the domain name. If you open it over plain http or by the server's IP address, the page may display but you cannot log in. That is deliberate, for security.

Day-to-day maintenance

All the commands below are run in the code directory, /srv/vallotton/app.

Vallotton also has an admin backend that shows every conversation, message and picture, and the API spend per day. For security it is not exposed by default, and you will not normally need it. If you really do, create a docker-compose.override.yml in the code directory that gives backend ports: ["127.0.0.1:8000:8000"], temporarily set COOKIE_SECURE=0 in .env, and restart the service. Then connect to the server through an SSH tunnel, open http://127.0.0.1:8000/admin/ and log in with ADMIN_USERNAME and ADMIN_PASSWORD. Remember to undo both changes when you are done.

MCP

MCP is an interface Vallotton provides for reaching its internal services through a URL. Other programs can use it to send a line of text and get the finished picture back, or to draw directly from a prompt, check the balance, and so on. Conversations started through MCP also show up in the left bar of the web page.

Note that this interface uses Vallotton's own simplified, home-grown variant of the protocol, not standard MCP, so it cannot be connected directly from clients like Claude Desktop or Cursor (MCP is not that common these days anyway...).

To enable it:

  1. Set MCP_TOKEN in .env to any string you like. This is the key to your Vallotton's MCP service.
  2. Run docker compose --profile mcp up -d --build. You can check with curl -s http://127.0.0.1:8301/health; if the response contains "ok": true, it is up.
  3. By default MCP listens only on port 8301 of the server itself. To reach it from the internet, set MCP_BIND=0.0.0.0 in .env and run the previous command again. Remember to open port 8301 in your cloud provider's console.

To use MCP, just give your coding agent the following information, and it will figure out on its own how to talk to Vallotton's MCP service.

{
  "name": "vallotton",
  "url": "http://SERVER_IP:8301",
  "bearer_token": "the MCP_TOKEN from your .env"
}

Two things to keep in mind. First, if you open it to the internet, MCP is reached from outside over plain HTTP. Second, a picture can take several minutes, so set the client's timeout to 600 seconds or more.

About security

If you deploy by this guide, Vallotton already has these protections by default:

What you have to take care of yourself: use long random strings for all the passwords in .env; never share the .env file or commit it to git; and in your cloud provider's console open only 22, 80 and 443, plus the MCP port if you really do mean to expose it.

Appendix: if something goes wrong

A short checklist.

What you seeCause and what to do
The page opens, but you cannot log inCheck that you opened it as https://YOUR_DOMAIN; opening it by IP address or over http will not let you log in. If you use Cloudflare, check that the SSL/TLS mode is “Full (strict)”. A server clock that is far off causes this too.
Logging in says you are locked outEight wrong passwords in a row lock the login for 15 minutes. Wait a while and try again.
Starting says the port is in useChange VALLOTTON_HTTP_PORT in .env (or MCP_PUBLIC_PORT for MCP) to another port, run docker compose up -d, then change the port in the proxy_pass line of the nginx configuration to match and reload nginx.
A picture stops halfway with “The task was interrupted”Vallotton was restarted while painting, for example right as you updated to a new version. Send the message again.
It stays at “is thinking it over” for everThe language model's official endpoint cannot be reached. You can set up the backup route with OPENROUTER_API_KEY in .env; when the official endpoint is down, Vallotton switches to the backup route by itself. “Routes” on the Settings page shows which one is in use at the moment.
Painting fails and the error mentions “402”The NovelAI account's points (Anlas) are used up, usually because the quality was set to “High”. Set it back to “Mid” in the generation settings.
NovelAI shows “status unknown”Now and then NovelAI's account status cannot be fetched. It does not affect painting and recovers by itself in a few minutes.
The MCP client times outSet the client's timeout to 600 seconds or more. A drawing request only returns once the picture is finished.

Appendix: environment variables

Below are the items that can be set in .env, grouped by purpose. “Default” is the value used when the item is left out. Items marked “required” have no default and must be given a value; without one, Vallotton will not start, or the feature in question will not work.

The example values in .env.example may differ from the defaults; what counts is what is actually written in .env. For a deployment you normally only need to change the items listed in step 2.

VariableDefaultDescription
Language model
LLM_PROVIDERdeepseekWhose language model to use: deepseek or grok.
DEEPSEEK_API_KEY—DeepSeek key. Required when using DeepSeek.
DEEPSEEK_MODELdeepseek-flashWhich DeepSeek model to use.
DEEPSEEK_REASONING_EFFORThighDeepSeek's reasoning effort when understanding the conversation and planning the picture: low, high or max. max more than doubles the wait for every picture without making it better.
DEEPSEEK_DRAW_REASONING_EFFORThighDeepSeek's reasoning effort when writing the drawing prompt; same values.
DEEPSEEK_MAX_TOKENS128000The most tokens one answer may contain. Set it too small and answers get cut off easily.
DEEPSEEK_RETRIES2How many times to retry automatically when DeepSeek's answer is empty or malformed (DeepSeek has formatting hiccups now and then).
DEEPSEEK_CONTEXT_LIMIT1000000DeepSeek's context limit.
DEEPSEEK_LONG_CONTEXT_THRESHOLD400000The conversation context length at which you get a reminder, once, when using DeepSeek.
OPENROUTER_API_KEYemptyOpenRouter key. DeepSeek's official service is not always stable; with an OpenRouter key set, a timeout on the official endpoint switches automatically to the same model on OpenRouter.
OPENROUTER_BASEhttps://openrouter.ai/api/v1Address of the OpenRouter backup route.
OPENROUTER_MODELdeepseek/deepseek-v4.1-flashThe model used on the OpenRouter backup route.
LLM_ROAD_BLOCKED_SECONDS1800After the official endpoint fails, how many seconds at most to route around it.
LLM_ROAD_PROBE_SECONDS60While routing around the official endpoint, how many seconds between attempts to use it again.
GROK_API_KEY—xAI key. Required when using Grok.
GROK_MODELgrok-4.6Which Grok model to use.
GROK_REASONING_EFFORThighGrok's reasoning effort when understanding the conversation and planning the picture. high corresponds to what xAI calls the expert level.
GROK_DRAW_REASONING_EFFORTmediumGrok's reasoning effort when writing the drawing prompt.
GROK_TITLE_MODELgrok-4.20-non-reasoningThe model Grok uses to give conversations their titles automatically. Leave it empty to use GROK_MODEL.
(Incidentally, with DeepSeek the titles are written by DEEPSEEK_MODEL directly and there is no separate setting, because DeepSeek is cheap anyway.)
GROK_CONTEXT_LIMIT500000Grok's context limit.
GROK_LONG_CONTEXT_THRESHOLD200000The conversation context length at which you get a reminder, once, when using Grok.
GROK_MAX_TOKENS20000The most tokens one Grok answer may contain.
GROK_CONTEXT_WARN_RATIO0.8At what fraction of the context the warnings begin. This one applies whichever provider you use.
LLM_TIMEOUT_SECONDS420How many seconds without any output from the model count as a timeout.
LLM_ANSWER_SECONDS600How many seconds at most to wait for one answer; beyond that the question is asked again.
LEAD_TOOL_ROUNDS2How many times at most the tag table is looked up while understanding the conversation and planning the picture. 0 means no lookups.
DRAW_TOOL_ROUNDS4How many times at most the tag table is looked up while writing the picture into a prompt. 0 means no lookups.
LEAD_USAGE_SCENARIO
DRAW_USAGE_SCENARIO
emptyThe “usage scenario” passage at the start of the system prompt.
DRAW_TEMPERATURE0The randomness when writing the picture into a prompt. This step is just translation and needs no creativity, so the default is 0.
Drawing
NOVELAI_API_KEY—NovelAI key, starting with pst-.
NAI_MODELnai-diffusion-5-fullThe NovelAI model used at Mid and High quality (Low quality always uses the previous generation, V4.5).
Reflection and continuous generation
MAX_REFLECTION_ROUNDS10The highest value “Reflection rounds” can be set to.
MAX_CONTINUOUS_ROUNDS20The highest value “Continuous” can be set to.
FREE_RETRIES_IN_RUN2How many retries when reflection or continuous generation fails midway.
JOB_STALE_SECONDS180How many seconds a task may go without progress before it counts as interrupted.
JOB_SWEEP_SECONDS60How many seconds between checks for interrupted tasks; the same check also sends the next line of a continuous generation. 0 means no checks (and then continuous generation does not continue either).
Accounts
APP_USERNAMEuserUsername for the web login.
APP_PASSWORD—Password for the web login.
ADMIN_USERNAMEadminUsername for the admin backend.
ADMIN_PASSWORD—Password for the admin backend.
Site
DJANGO_SECRET_KEYan insecure placeholderThe key that protects the login state. Change it to a string of random characters when deploying. It starts without the change, but is not secure.
DOMAINemptyYour domain name. Logging in is only possible through this domain.
DJANGO_DEBUG0Debug switch: 1 is on, 0 is off. Keep it at 0 when deploying. If you deploy by this guide, compose already pins it to 0, and changing it has no effect.
COOKIE_SECURE1At 1, the login state travels only over https. Set it to 0 only while the admin backend is temporarily enabled, and change it back afterwards.
TRUSTED_PROXY_HOPS1How many layers of reverse proxy sit in front of Vallotton on the server. Deployed by this guide, it is 1 and normally needs no change. Getting the count wrong disables the login attempt limit.
Database and pictures
POSTGRES_PASSWORD—Database password; a string of random characters is fine.
POSTGRES_DB
POSTGRES_USER
vallottonDatabase name and username; normally no need to change.
POSTGRES_HOST
POSTGRES_PORT
127.0.0.1
5432
Database address and port. Deployed by this guide, compose always connects to its own database container, so no change is needed. Only used when running the backend outside docker.
MEDIA_DIR./tmp/mediaThe directory on the server where pictures are kept. Use an absolute path when deploying.
Ports and MCP
VALLOTTON_HTTP_PORT8300The port Vallotton opens on the server itself; nginx forwards visits here. No need to change it unless it clashes with another application's port.
MCP_PUBLIC_PORT8301MCP's port on the server. Only used when MCP is enabled.
MCP_BIND127.0.0.1Who the MCP port is open to. 127.0.0.1 means only the server itself; 0.0.0.0 means it can be reached directly from outside.
MCP_TOKEN—The key that MCP requests must carry. Required when MCP is enabled.
MCP_HOST
MCP_PORT
127.0.0.1
8766
The listen address and port when running mcp/server.py on its own outside docker. No change needed with compose.
VALLOTTON_BACKEND_URLhttp://127.0.0.1:8000Which backend MCP forwards requests to. With compose it points at the backend container automatically; no change needed.

A few variables used only in development and testing (such as switching the database to sqlite, or painting placeholder images instead of calling NovelAI) are not listed here.