How to Enable Persistent Cross-Session Memory in DeepSeek Harness on a Server
If you use DeepSeek Harness for long-running projects, one problem appears quickly.
A conversation eventually reaches its context limit. You start a new chat the next day, but you still want DeepSeek to remember the project background, previous decisions, current progress, important parameters, and where your source materials are stored.
The goal is simple.
Conversation 1 reaches its context limit
↓
Important information is saved outside the chat
↓
Start Conversation 2
↓
Relevant memory is loaded automatically
↓
Continue working without re-uploading everything
When DeepSeek Harness runs on your local computer, this is mainly a memory-plugin problem.
When Harness runs on a remote Linux server behind Nginx, two additional issues appear frequently.
forbidden: loopback-only
and
dsh web authentication required; reopen the URL printed by dsh web.
This guide walks through the full setup, including persistent memory, Nginx reverse proxy configuration, loopback restrictions, browser authentication, and a practical test for cross-session memory.
What Persistent Memory Should Actually Save
Persistent memory works best when it preserves the information required to continue a project. Trying to copy an entire 1-million-token conversation into the next context window simply consumes the new window again.
A better model is to extract and store the state of the project.
Useful long-term memory can include
-
Project background -
Completed work -
Important decisions -
Rejected approaches -
Configuration values -
User preferences -
Locations of source materials -
Current project status -
Next actions
The workflow then looks like this.
Conversation 1
1 million tokens
↓
Extract important information
↓
Persistent memory
↓
Close Conversation 1
↓
Start Conversation 2
↓
Load relevant memory
↓
Continue the same project
The new conversation does not need every sentence from the previous session.
It needs enough information to understand the current state of the work. When a specific historical detail is required, that detail can be retrieved separately from memory or from an external document store.
Install a Long-Term Memory Plugin in DeepSeek Harness
A practical first step is to install one memory plugin and verify that it survives across sessions.
One option is
dsh-plugin-memory
Install it with
dsh plugin --profile web add dsh-plugin-memory
Then restart Harness.
dsh web
The purpose of this type of plugin is to save memory outside the active chat session so a later session can load it again.
Another option considered for this workflow is
dsh-auto-memory
That approach focuses more heavily on automatically extracting user information, project status, and ongoing memory from conversations.
If you already installed a plugin marketplace, you can also search for Memory-related plugins from the Harness interface.
The marketplace itself can be installed with
dsh plugin --profile web add dshmarket
Then start the web interface.
dsh web
Avoid Installing Several Memory Plugins at Once
Running multiple memory plugins together makes troubleshooting harder.
Several plugins may inject the same project information into the context. Their summaries may also differ, which can create conflicting project state.
Repeated memory injection also consumes tokens.
Start with one plugin and test the complete flow before adding anything else.
A minimal test looks like this.
In the first conversation, save an explicit fact.
Remember that the current test project is called Project ABC.
Create a new conversation and ask
What was the name of our previous test project?
If the new session answers Project ABC, basic cross-session memory is working.
Keep Long-Term Memory Separate from Large Source Documents
A memory plugin is suitable for project state and important conclusions.
It is less suitable for storing hundreds of PDFs, large Word files, source-code repositories, spreadsheets, or a complete archive of every previous conversation.
A more practical architecture separates memory from source materials.
DeepSeek Harness
│
├─ Persistent memory
│ │
│ ├─ Project background
│ ├─ Decisions
│ ├─ Progress
│ └─ Next actions
│
└─ Source materials
│
├─ PDF files
├─ Word documents
├─ Source code
└─ Historical material
Persistent memory answers questions such as what the project is doing, which decisions were made, and what should happen next.
The source-material layer keeps the original documents available when exact details are needed.
This separation lets one conversation consume a very large context window while the next conversation starts with a compact description of the project state.
Why DeepSeek Harness Shows forbidden: loopback-only
A typical remote deployment looks like this.
Browser
↓
https://dsh.example.com
↓
Nginx
↓
127.0.0.1:3080
↓
DeepSeek Harness
The important part is that Harness continues listening only on the local loopback interface.
127.0.0.1:3080
Nginx exposes HTTPS on port 443 and proxies requests to Harness locally.
A common Linux layout may use a site configuration such as
/etc/nginx/sites-available/dsh.example.com
with an enabled symlink under
/etc/nginx/sites-enabled/dsh.example.com
If Let’s Encrypt is used, certificate paths may look like
/etc/letsencrypt/live/dsh.example.com/fullchain.pem
/etc/letsencrypt/live/dsh.example.com/privkey.pem
The Harness listener should remain similar to
LISTEN 127.0.0.1:3080
There is usually no reason to expose port 3080 directly to the public internet.
How to Inspect the Existing Nginx Configuration
Before changing anything, find the current domain configuration, confirm the backend port, and verify that Harness is responding locally.
Replace dsh.example.com with your own domain.
DOMAIN="dsh.example.com"
echo "===== NGINX VERSION ====="
nginx -v 2>&1
echo
echo "===== CONFIG FILES CONTAINING DOMAIN ====="
sudo grep -Rni "$DOMAIN" /etc/nginx 2>/dev/null
echo
echo "===== ENABLED SITES ====="
sudo ls -lah /etc/nginx/sites-enabled/ 2>/dev/null
echo
echo "===== CONF.D ====="
sudo ls -lah /etc/nginx/conf.d/ 2>/dev/null
echo
echo "===== DOMAIN CONFIG ====="
sudo nginx -T 2>&1 | grep -n -B 20 -A 100 "server_name.*$DOMAIN"
echo
echo "===== PORT 3080 ====="
sudo ss -lntp | grep ':3080'
echo
echo "===== HARNESS LOCAL TEST ====="
curl -I http://127.0.0.1:3080 2>&1 | head -30
A response such as
HTTP/1.1 401 Unauthorized
does not automatically mean Harness is down.
If port 3080 is listening and the server returns an HTTP response, the request is reaching Harness.
If the browser later reports that dsh web authentication is required, that provides another indication that the web service itself is running.
Why the Original Nginx Host Header Can Trigger the Loopback Error
A common reverse-proxy configuration contains this line.
proxy_set_header Host $host;
When the browser opens
https://dsh.example.com
Nginx forwards the same hostname upstream.
Harness therefore sees
dsh.example.com
as the Host value.
Some settings, credentials, and plugin-related functions may then return
forbidden: loopback-only
One way to make the upstream request use the local Harness authority is to send 127.0.0.1:3080 to the backend.
Back up the current site configuration first.
DOMAIN="dsh.example.com"
sudo cp "/etc/nginx/sites-available/$DOMAIN" \
"/etc/nginx/sites-available/$DOMAIN.bak-$(date +%Y%m%d-%H%M%S)"
Confirm the backup before editing.
ls -lh /etc/nginx/sites-available/dsh.example.com*
Example Nginx Configuration for DeepSeek Harness
The following example uses dsh.example.com as a placeholder.
Replace it with your own hostname and confirm that the certificate paths match your server.
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name dsh.example.com;
ssl_certificate /etc/letsencrypt/live/dsh.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dsh.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
client_max_body_size 500m;
location / {
proxy_pass http://127.0.0.1:3080;
proxy_http_version 1.1;
proxy_set_header Host 127.0.0.1:3080;
proxy_set_header Origin http://127.0.0.1:3080;
proxy_set_header Referer http://127.0.0.1:3080/;
proxy_set_header Sec-Fetch-Site same-origin;
proxy_set_header Sec-Fetch-Mode cors;
proxy_set_header Sec-Fetch-Dest empty;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 60s;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_request_buffering off;
proxy_buffering off;
}
}
server {
listen 80;
listen [::]:80;
server_name dsh.example.com;
return 301 https://dsh.example.com$request_uri;
}
Open the site configuration with an editor.
sudo nano /etc/nginx/sites-available/dsh.example.com
After saving the file, validate the Nginx configuration.
sudo nginx -t
A successful result should look similar to
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Reload Nginx only after the syntax test succeeds.
sudo systemctl reload nginx
Check its status afterward.
sudo systemctl status nginx --no-pager
Keep Harness bound to
127.0.0.1:3080
Do not change it to
0.0.0.0:3080
simply to make remote access easier. In this architecture, Nginx is already responsible for public access.
Fix dsh web authentication required
After the loopback issue is addressed, the browser may show
dsh web authentication required; reopen the URL printed by dsh web.
At this point the reverse proxy is reaching Harness, but the browser has not completed the dsh web authentication flow.
When dsh web starts, it prints a URL containing a token.
It may look like this.
http://127.0.0.1:3080/?token=<REDACTED_TOKEN>
A remote browser cannot open the server’s own 127.0.0.1.
Keep the token value and replace the local address with the public HTTPS hostname.
If the server prints
http://127.0.0.1:3080/?token=<REDACTED_TOKEN>
open this in the browser.
https://dsh.example.com/?token=<REDACTED_TOKEN>
After the initial authentication succeeds, the browser can continue using
https://dsh.example.com
Treat the token as sensitive information.
Do not publish it in screenshots, logs, articles, repositories, or support messages.
How to Find the DeepSeek Harness Authentication URL
If Harness is running in the background, start by checking the system journal.
sudo journalctl --no-pager | grep 'dsh web:' | tail -20
You may see a line similar to
dsh web: http://127.0.0.1:3080/?token=<REDACTED_TOKEN>
Replace only the local host portion when opening the URL remotely.
If the command returns nothing, identify how the Harness process was started.
First find the process listening on port 3080.
sudo ss -lntp | grep ':3080'
The output may contain something similar to
users:(("node",pid=<PID>,fd=<FD>))
Use the reported PID in the following commands.
PID="<PID>"
ps -fp "$PID"
Inspect the full process command line.
sudo tr '\0' ' ' < "/proc/$PID/cmdline"
echo
Check its parent process.
ps -o pid,ppid,user,lstart,cmd -p "$PID"
Inspect the process tree.
pstree -sp "$PID"
You can also check whether the process belongs to a systemd unit.
sudo systemctl status "$PID" --no-pager 2>&1 | head -80
These checks help identify whether Harness is running through one of the following methods.
systemd
PM2
nohup
Docker
screen
tmux
direct dsh web process
Once the startup method is known, the original dsh web output can usually be found in the corresponding logs.
Add the Public Domain as a Trusted Host
If you control the Harness startup command, you can also add the public hostname as a trusted host.
dsh web \
--no-open \
--port 3080 \
--trusted-host dsh.example.com \
--trusted-host dsh.example.com:443
Trusted-host configuration and browser token authentication handle different parts of the remote-access flow.
A trusted host alone does not establish a browser authentication session.
That means the browser may still show
dsh web authentication required
until the token URL has been opened successfully.
For troubleshooting, handle each layer in order.
1. Confirm 127.0.0.1:3080 is listening
2. Confirm Nginx can proxy to Harness
3. Resolve loopback-related access
4. Complete browser token authentication
5. Test the memory plugin
Changing several layers at the same time makes it much harder to identify which part is failing.
How to Test Cross-Session Memory Properly
Once Nginx and browser authentication are working, test the actual requirement.
In Conversation A, save several explicit pieces of project state.
Please remember the following information.
The current project is called Project ABC.
The server deployment and Nginx reverse proxy are complete.
The next task is to test persistent cross-session memory.
Confirm that the memory plugin has saved the information.
Then create Conversation B and ask
What is the current project called?
What work have we already completed?
What should we do next?
If the new conversation can answer those questions without receiving the information again, the persistent-memory path is working.
Conversation A
↓
Memory plugin
↓
Persistent server-side memory
↓
Close old conversation
↓
Create Conversation B
↓
Load project memory
↓
Continue working
This is the behavior that matters.
The old conversation can reach its context limit while the next session still understands the project state.
Make Sure Memory Survives Server Restarts
When Harness is installed directly on a Linux host, memory files stored on the server normally remain available as long as the underlying directories are preserved.
Docker adds another failure mode.
If memory, workspace, session, or plugin data exists only inside the container filesystem, deleting or recreating the container can remove it.
A server deployment therefore needs several forms of persistence.
| Data | What needs to persist |
|---|---|
| Active chat context | Limited by the model context window |
| Long-term memory | Memory plugin data |
| PDFs and source documents | Persistent document storage |
| Harness configuration | Server-side configuration files |
| Docker data | Mounted persistent volumes |
| Browser access | dsh web authentication session |
For long-running projects, chat history alone is not enough. The project state and source material need storage outside the active model context.
What to Remove Before Publishing Your Configuration
Server troubleshooting posts can expose more information than expected.
Before publishing screenshots, logs, or configuration files, remove or replace the following values.
-
Real production domains -
Public IP addresses -
SSH usernames -
API keys -
Harness startup tokens -
Browser cookies -
Authorization headers -
Cloudflare tokens -
Database passwords -
Private keys -
.envcontents -
Internal hostnames -
Environment-specific private paths -
Current process IDs when they are unnecessary
Generic system paths can usually remain in technical documentation.
Examples include
/etc/nginx/sites-available/
/etc/letsencrypt/live/
127.0.0.1:3080
Use placeholders for environment-specific values.
dsh.example.com
203.0.113.x
<SSH_USER>
<PID>
<API_KEY>
<REDACTED_TOKEN>
DeepSeek Harness Persistent Memory Setup Checklist
Use this checklist when deploying Harness behind Nginx.
-
Keep Harness bound to 127.0.0.1:3080 -
Expose the service through HTTPS on Nginx -
Back up the Nginx site configuration before editing -
Keep the upstream target on http://127.0.0.1:3080 -
Adjust upstream Host, Origin, and Referer when required by the loopback setup -
Run sudo nginx -t -
Reload Nginx only after the test succeeds -
Find the token URL printed by dsh web -
Replace the local address with your HTTPS hostname -
Open the token URL once to establish browser authentication -
Keep the token private -
Install one long-term memory plugin first -
Save an explicit test memory in the old session -
Create a new session and verify memory recall -
Keep large PDFs, codebases, and original documents outside the chat context -
Persist memory and workspace data when using Docker
FAQ
Why does DeepSeek Harness return 401 Unauthorized on 127.0.0.1:3080
A 401 response can mean that Harness is running but the request has not completed the browser authentication flow.
If the port is listening and the server responds to HTTP requests, continue by checking the dsh web authentication URL.
Why does the web interface work while Settings shows forbidden: loopback-only
A common cause is that Nginx forwards the public hostname to Harness.
For example
proxy_set_header Host $host;
sends the external hostname upstream.
The configuration in this guide uses
proxy_set_header Host 127.0.0.1:3080;
along with corresponding Origin and Referer headers.
Why did authentication required appear after fixing Nginx
That usually means the request reached Harness successfully and the browser now needs to complete the dsh web token authentication step.
Find the startup URL containing ?token= and open the equivalent URL through your HTTPS domain.
Should the dsh web token be stored in the public Nginx configuration
No.
Treat it as sensitive authentication material. Keep it out of public configuration examples, screenshots, repositories, and documentation.
Does a memory plugin save the full 1-million-token conversation
The useful goal is to preserve the information required for future sessions.
Project background, decisions, progress, and next actions are good candidates for long-term memory.
Large original documents and historical source material should remain in external persistent storage and be retrieved when needed.
Do I need to open the token URL every time I create a new conversation
Creating a new Harness conversation and authenticating the browser are separate actions.
As long as the browser authentication session remains valid, starting a new chat does not require another token login.
Should DeepSeek Harness listen on 0.0.0.0:3080
The deployment described here does not require it.
Keeping Harness on 127.0.0.1:3080 allows Nginx to remain the public entry point while the Harness backend stays local to the server.
How do I know that persistent memory is really working
Do not use plugin installation status as the final test.
Save a specific project name, completed task, and next action in one conversation. Create a new conversation and ask for those values without providing them again.
If the new session retrieves them correctly, cross-session memory is working.

